CRIS: Drop support for the CRIS port

The port was added back in 2000 so it's no longer even a good source
of inspiration for newer ports (if it ever was)

The last SoC (ARTPEC-3) with a CRIS main CPU was launched in 2008.

Coupled with time and working developer board hardware being
in low supply, it's time to drop the port from Linux.

So long and thanks for all the fish!

Signed-off-by: Jesper Nilsson <jesper.nilsson@axis.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Jesper Nilsson 2018-03-11 11:05:23 +01:00 committed by Arnd Bergmann
parent bb9d812643
commit c690eddc2f
430 changed files with 0 additions and 99983 deletions

View File

@ -112,8 +112,6 @@ cputopology.txt
- documentation on how CPU topology info is exported via sysfs.
crc32.txt
- brief tutorial on CRC computation
cris/
- directory with info about Linux on CRIS architecture.
crypto/
- directory with info on the Crypto API.
dcdbas.txt

View File

@ -1,195 +0,0 @@
Linux on the CRIS architecture
==============================
This is a port of Linux to Axis Communications ETRAX 100LX,
ETRAX FS and ARTPEC-3 embedded network CPUs.
For more information about CRIS and ETRAX please see further below.
In order to compile this you need a version of gcc with support for the
ETRAX chip family. Please see this link for more information on how to
download the compiler and other tools useful when building and booting
software for the ETRAX platform:
http://developer.axis.com/wiki/doku.php?id=axis:install-howto-2_20
What is CRIS ?
--------------
CRIS is an acronym for 'Code Reduced Instruction Set'. It is the CPU
architecture in Axis Communication AB's range of embedded network CPU's,
called ETRAX.
The ETRAX 100LX chip
--------------------
For reference, please see the following link:
http://www.axis.com/products/dev_etrax_100lx/index.htm
The ETRAX 100LX is a 100 MIPS processor with 8kB cache, MMU, and a very broad
range of built-in interfaces, all with modern scatter/gather DMA.
Memory interfaces:
* SRAM
* NOR-flash/ROM
* EDO or page-mode DRAM
* SDRAM
I/O interfaces:
* one 10/100 Mbit/s ethernet controller
* four serial-ports (up to 6 Mbit/s)
* two synchronous serial-ports for multimedia codec's etc.
* USB host controller and USB slave
* ATA
* SCSI
* two parallel-ports
* two generic 8-bit ports
(not all interfaces are available at the same time due to chip pin
multiplexing)
ETRAX 100LX is CRISv10 architecture.
The ETRAX FS and ARTPEC-3 chips
-------------------------------
The ETRAX FS is a 200MHz 32-bit RISC processor with on-chip 16kB
I-cache and 16kB D-cache and with a wide range of device interfaces
including multiple high speed serial ports and an integrated USB 1.1 PHY.
The ARTPEC-3 is a variant of the ETRAX FS with additional IO-units
used by the Axis Communications network cameras.
See below link for more information:
http://www.axis.com/products/dev_etrax_fs/index.htm
ETRAX FS and ARTPEC-3 are both CRISv32 architectures.
Bootlog
-------
Just as an example, this is the debug-output from a boot of Linux 2.4 on
a board with ETRAX 100LX. The displayed BogoMIPS value is 5 times too small :)
At the end you see some user-mode programs booting like telnet and ftp daemons.
Linux version 2.4.1 (bjornw@godzilla.axis.se) (gcc version 2.96 20000427 (experimental)) #207 Wed Feb 21 15:48:15 CET 2001
ROM fs in RAM, size 1376256 bytes
Setting up paging and the MMU.
On node 0 totalpages: 2048
zone(0): 2048 pages.
zone(1): 0 pages.
zone(2): 0 pages.
Linux/CRIS port on ETRAX 100LX (c) 2001 Axis Communications AB
Kernel command line:
Calibrating delay loop... 19.91 BogoMIPS
Memory: 13872k/16384k available (587k kernel code, 2512k reserved, 44k data, 24k init)
kmem_create: Forcing size word alignment - vm_area_struct
kmem_create: Forcing size word alignment - filp
Dentry-cache hash table entries: 2048 (order: 1, 16384 bytes)
Buffer-cache hash table entries: 2048 (order: 0, 8192 bytes)
Page-cache hash table entries: 2048 (order: 0, 8192 bytes)
kmem_create: Forcing size word alignment - kiobuf
kmem_create: Forcing size word alignment - bdev_cache
Inode-cache hash table entries: 1024 (order: 0, 8192 bytes)
kmem_create: Forcing size word alignment - inode_cache
POSIX conformance testing by UNIFIX
Linux NET4.0 for Linux 2.4
Based upon Swansea University Computer Society NET3.039
Starting kswapd v1.8
kmem_create: Forcing size word alignment - file lock cache
kmem_create: Forcing size word alignment - blkdev_requests
block: queued sectors max/low 9109kB/3036kB, 64 slots per queue
ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000 Axis Communications AB
eth0 initialized
eth0: changed MAC to 00:40:8C:CD:00:00
ETRAX 100LX serial-driver $Revision: 1.7 $, (c) 2000 Axis Communications AB
ttyS0 at 0xb0000060 is a builtin UART with DMA
ttyS1 at 0xb0000068 is a builtin UART with DMA
ttyS2 at 0xb0000070 is a builtin UART with DMA
ttyS3 at 0xb0000078 is a builtin UART with DMA
Axis flash mapping: 200000 at 50000000
Axis flash: Found 1 x16 CFI device at 0x0 in 16 bit mode
Amd/Fujitsu Extended Query Table v1.0 at 0x0040
Axis flash: JEDEC Device ID is 0xC4. Assuming broken CFI table.
Axis flash: Swapping erase regions for broken CFI table.
number of CFI chips: 1
Using default partition table
I2C driver v2.2, (c) 1999-2001 Axis Communications AB
ETRAX 100LX GPIO driver v2.1, (c) 2001 Axis Communications AB
NET4: Linux TCP/IP 1.0 for NET4.0
IP Protocols: ICMP, UDP, TCP
kmem_create: Forcing size word alignment - ip_dst_cache
IP: routing cache hash table of 1024 buckets, 8Kbytes
TCP: Hash tables configured (established 2048 bind 2048)
NET4: Unix domain sockets 1.0/SMP for Linux NET4.0.
VFS: Mounted root (cramfs filesystem) readonly.
Init starts up...
Mounted none on /proc ok.
Setting up eth0 with ip 10.13.9.116 and mac 00:40:8c:18:04:60
eth0: changed MAC to 00:40:8C:18:04:60
Setting up lo with ip 127.0.0.1
Default gateway is 10.13.9.1
Hostname is bbox1
Telnetd starting, using port 23.
using /bin/sash as shell.
sftpd[15]: sftpd $Revision: 1.7 $ starting up
And here is how some /proc entries look:
17# cd /proc
17# cat cpuinfo
cpu : CRIS
cpu revision : 10
cpu model : ETRAX 100LX
cache size : 8 kB
fpu : no
mmu : yes
ethernet : 10/100 Mbps
token ring : no
scsi : yes
ata : yes
usb : yes
bogomips : 99.84
17# cat meminfo
total: used: free: shared: buffers: cached:
Mem: 7028736 925696 6103040 114688 0 229376
Swap: 0 0 0
MemTotal: 6864 kB
MemFree: 5960 kB
MemShared: 112 kB
Buffers: 0 kB
Cached: 224 kB
Active: 224 kB
Inact_dirty: 0 kB
Inact_clean: 0 kB
Inact_target: 0 kB
HighTotal: 0 kB
HighFree: 0 kB
LowTotal: 6864 kB
LowFree: 5960 kB
SwapTotal: 0 kB
SwapFree: 0 kB
17# ls -l /bin
-rwxr-xr-x 1 342 100 10356 Jan 01 00:00 ifconfig
-rwxr-xr-x 1 342 100 17548 Jan 01 00:00 init
-rwxr-xr-x 1 342 100 9488 Jan 01 00:00 route
-rwxr-xr-x 1 342 100 46036 Jan 01 00:00 sftpd
-rwxr-xr-x 1 342 100 48104 Jan 01 00:00 sh
-rwxr-xr-x 1 342 100 16252 Jan 01 00:00 telnetd

View File

@ -1,9 +0,0 @@
Axis Communications AB
ARTPEC series SoC Device Tree Bindings
CRISv32 based SoCs are ETRAX FS and ARTPEC-3:
- compatible = "axis,crisv32";

View File

@ -1,8 +0,0 @@
Boards based on the CRIS SoCs:
Required root node properties:
- compatible = should be one or more of the following:
- "axis,dev88" - for Axis devboard 88 with ETRAX FS
Optional:

View File

@ -1,23 +0,0 @@
* CRISv32 Interrupt Controller
Interrupt controller for the CRISv32 SoCs.
Main node required properties:
- compatible : should be:
"axis,crisv32-intc"
- interrupt-controller : Identifies the node as an interrupt controller
- #interrupt-cells : Specifies the number of cells needed to encode an
interrupt source. The type shall be a <u32> and the value shall be 1.
- reg: physical base address and size of the intc registers map.
Example:
intc: interrupt-controller {
compatible = "axis,crisv32-intc";
reg = <0xb001c000 0x1000>;
interrupt-controller;
#interrupt-cells = <1>;
};

View File

@ -3704,16 +3704,6 @@ S: Maintained
F: Documentation/filesystems/cramfs.txt
F: fs/cramfs/
CRIS PORT
M: Mikael Starvik <starvik@axis.com>
M: Jesper Nilsson <jesper.nilsson@axis.com>
L: linux-cris-kernel@axis.com
W: http://developer.axis.com
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jesper/cris.git
S: Maintained
F: arch/cris/
F: drivers/tty/serial/crisv10.*
CRYPTO API
M: Herbert Xu <herbert@gondor.apana.org.au>
M: "David S. Miller" <davem@davemloft.net>

View File

@ -1,595 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
config MMU
bool
default y
config ZONE_DMA
bool
default y
config RWSEM_GENERIC_SPINLOCK
bool
default y
config RWSEM_XCHGADD_ALGORITHM
bool
config ARCH_HAS_ILOG2_U32
bool
default n
config ARCH_HAS_ILOG2_U64
bool
default n
config GENERIC_HWEIGHT
bool
default y
config GENERIC_CALIBRATE_DELAY
bool
default y
config NO_IOPORT_MAP
def_bool y if !PCI
config NO_DMA
def_bool y if !PCI
config FORCE_MAX_ZONEORDER
int
default 6
config TRACE_IRQFLAGS_SUPPORT
depends on ETRAX_ARCH_V32
def_bool y
config STACKTRACE_SUPPORT
def_bool y
config LOCKDEP_SUPPORT
depends on ETRAX_ARCH_V32
def_bool y
config CRIS
bool
default y
select HAVE_IDE
select GENERIC_ATOMIC64
select HAVE_UID16
select VIRT_TO_BUS
select ARCH_WANT_IPC_PARSE_VERSION
select GENERIC_IRQ_SHOW
select GENERIC_IOMAP
select MODULES_USE_ELF_RELA
select CLONE_BACKWARDS2
select HAVE_EXIT_THREAD if ETRAX_ARCH_V32
select OLD_SIGSUSPEND
select OLD_SIGACTION
select GPIOLIB
select IRQ_DOMAIN if ETRAX_ARCH_V32
select OF if ETRAX_ARCH_V32
select OF_EARLY_FLATTREE if ETRAX_ARCH_V32
select CLKSRC_MMIO if ETRAX_ARCH_V32
select GENERIC_CLOCKEVENTS if ETRAX_ARCH_V32
select GENERIC_SCHED_CLOCK if ETRAX_ARCH_V32
select HAVE_DEBUG_BUGVERBOSE if ETRAX_ARCH_V32
select HAVE_NMI
select DMA_DIRECT_OPS if PCI
config HZ
int
default 100
config NR_CPUS
int
default "1"
config BUILTIN_DTB
string "DTB to build into the kernel image"
depends on OF
source "init/Kconfig"
source "kernel/Kconfig.freezer"
menu "General setup"
source "fs/Kconfig.binfmt"
config ETRAX_CMDLINE
string "Kernel command line"
default "root=/dev/mtdblock3"
help
Pass additional commands to the kernel.
config ETRAX_WATCHDOG
bool "Enable ETRAX watchdog"
help
Enable the built-in watchdog timer support on ETRAX based embedded
network computers.
config ETRAX_WATCHDOG_NICE_DOGGY
bool "Disable watchdog during Oops printouts"
depends on ETRAX_WATCHDOG
help
By enabling this you make sure that the watchdog does not bite while
printing oopses. Recommended for development systems but not for
production releases.
config ETRAX_FAST_TIMER
bool "Enable ETRAX fast timer API"
help
This options enables the API to a fast timer implementation using
timer1 to get sub jiffie resolution timers (primarily one-shot
timers).
This is needed if CONFIG_ETRAX_SERIAL_FAST_TIMER is enabled.
config ETRAX_KMALLOCED_MODULES
bool "Enable module allocation with kmalloc"
help
Enable module allocation with kmalloc instead of vmalloc.
source "kernel/Kconfig.preempt"
source mm/Kconfig
endmenu
menu "Hardware setup"
choice
prompt "Processor type"
default ETRAX100LX
config ETRAX100LX
bool "ETRAX-100LX-v1"
select ARCH_USES_GETTIMEOFFSET
help
Support version 1 of the ETRAX 100LX.
config ETRAX100LX_V2
bool "ETRAX-100LX-v2"
select ARCH_USES_GETTIMEOFFSET
help
Support version 2 of the ETRAX 100LX.
config ETRAXFS
bool "ETRAX-FS-V32"
help
Support CRIS V32.
config CRIS_MACH_ARTPEC3
bool "ARTPEC-3"
help
Support Axis ARTPEC-3.
endchoice
config ETRAX_ARCH_V10
bool
default y if ETRAX100LX || ETRAX100LX_V2
default n if !(ETRAX100LX || ETRAX100LX_V2)
select TTY
config ETRAX_ARCH_V32
bool
default y if (ETRAXFS || CRIS_MACH_ARTPEC3)
default n if !(ETRAXFS || CRIS_MACH_ARTPEC3)
config ETRAX_DRAM_SIZE
int "DRAM size (dec, in MB)"
default "8"
help
Size of DRAM (decimal in MB) typically 2, 8 or 16.
config ETRAX_VMEM_SIZE
int "Video memory size (dec, in MB)"
depends on ETRAX_ARCH_V32 && !ETRAXFS
default 8 if !ETRAXFS
help
Size of Video accessible memory (decimal, in MB).
config ETRAX_FLASH_BUSWIDTH
int "Buswidth of NOR flash in bytes"
default "2"
help
Width in bytes of the NOR Flash bus (1, 2 or 4). Is usually 2.
config ETRAX_FLASH1_SIZE
int "FLASH1 size (dec, in MB. 0 = Unknown)"
default "0"
choice
prompt "Product debug-port"
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
source arch/cris/arch-v10/Kconfig
source arch/cris/arch-v32/Kconfig
endmenu
source "net/Kconfig"
# bring in ETRAX built-in drivers
menu "Drivers for built-in interfaces"
source arch/cris/arch-v10/drivers/Kconfig
source arch/cris/arch-v32/drivers/Kconfig
config ETRAX_AXISFLASHMAP
bool "Axis flash-map support"
select MTD
select MTD_CFI
select MTD_CFI_AMDSTD
select MTD_JEDECPROBE if ETRAX_ARCH_V32
select MTD_BLOCK
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"
help
Select this to enable the synchronous serial port 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 && (ETRAXFS || ETRAX_ARCH_V10)
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.
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
choice
prompt "Ser0 DMA out channel"
depends on ETRAX_SERIAL_PORT0
default ETRAX_SERIAL_PORT0_DMA6_OUT if ETRAX_ARCH_V32
default ETRAX_SERIAL_PORT0_NO_DMA_OUT if ETRAX_ARCH_V10
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 ETRAXFS
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.
config ETRAX_SERIAL_PORT0_DMA0_OUT
bool "Ser0 uses DMA0 for output"
depends on CRIS_MACH_ARTPEC3
help
Enables the DMA0 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
choice
prompt "Ser0 DMA in channel "
depends on ETRAX_SERIAL_PORT0
default ETRAX_SERIAL_PORT0_NO_DMA_IN if ETRAX_ARCH_V32
default ETRAX_SERIAL_PORT0_DMA7_IN if ETRAX_ARCH_V10
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 ETRAXFS
help
Enables the DMA7 input channel for ser0 (ttyS0).
If you do not enable DMA, an interrupt for each character will be
used when receiving data.
Normally you want to use DMA, unless you use the DMA channel for
something else.
config ETRAX_SERIAL_PORT0_DMA1_IN
bool "Ser0 uses DMA1 for input"
depends on CRIS_MACH_ARTPEC3
help
Enables the DMA1 input channel for ser0 (ttyS0).
If you do not enable DMA, an interrupt for each character will be
used when receiving data.
Normally you want to use DMA, unless you use the DMA channel for
something else.
endchoice
choice
prompt "Ser1 DMA in channel "
depends on ETRAX_SERIAL_PORT1
default ETRAX_SERIAL_PORT1_NO_DMA_IN if ETRAX_ARCH_V32
default ETRAX_SERIAL_PORT1_DMA9_IN if ETRAX_ARCH_V10
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_ARCH_V32
help
Enables the DMA5 input channel for ser1 (ttyS1).
If you do not enable DMA, an interrupt for each character will be
used when receiving data.
Normally you want this on, unless you use the DMA channel for
something else.
config ETRAX_SERIAL_PORT1_DMA9_IN
depends on ETRAX_ARCH_V10
bool "Ser1 uses DMA9 for input"
endchoice
choice
prompt "Ser1 DMA out channel"
depends on ETRAX_SERIAL_PORT1
default ETRAX_SERIAL_PORT1_NO_DMA_OUT if ETRAX_ARCH_V32
default ETRAX_SERIAL_PORT1_DMA8_OUT if ETRAX_ARCH_V10
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_DMA8_OUT
depends on ETRAX_ARCH_V10
bool "Ser1 uses DMA8 for output"
config ETRAX_SERIAL_PORT1_DMA4_OUT
depends on ETRAX_ARCH_V32
bool "Ser1 uses DMA4 for output"
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
choice
prompt "Ser2 DMA out channel"
depends on ETRAX_SERIAL_PORT2
default ETRAX_SERIAL_PORT2_NO_DMA_OUT if ETRAX_ARCH_V32
default ETRAX_SERIAL_PORT2_DMA2_OUT if ETRAX_ARCH_V10
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 ETRAXFS || ETRAX_ARCH_V10
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.
config ETRAX_SERIAL_PORT2_DMA6_OUT
bool "Ser2 uses DMA6 for output"
depends on CRIS_MACH_ARTPEC3
help
Enables the DMA6 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
choice
prompt "Ser2 DMA in channel"
depends on ETRAX_SERIAL_PORT2
default ETRAX_SERIAL_PORT2_NO_DMA_IN if ETRAX_ARCH_V32
default ETRAX_SERIAL_PORT2_DMA3_IN if ETRAX_ARCH_V10
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 ETRAXFS || ETRAX_ARCH_V10
help
Enables the DMA3 input channel for ser2 (ttyS2).
If you do not enable DMA, an interrupt for each character will be
used when receiving data.
Normally you want to use DMA, unless you use the DMA channel for
something else.
config ETRAX_SERIAL_PORT2_DMA7_IN
bool "Ser2 uses DMA7 for input"
depends on CRIS_MACH_ARTPEC3
help
Enables the DMA7 input channel for ser2 (ttyS2).
If you do not enable DMA, an interrupt for each character will be
used when receiving data.
Normally you want to use DMA, unless you use the DMA channel for
something else.
endchoice
choice
prompt "Ser3 DMA in channel"
depends on ETRAX_SERIAL_PORT3
default ETRAX_SERIAL_PORT3_NO_DMA_IN if ETRAX_ARCH_V32
default ETRAX_SERIAL_PORT3_DMA5_IN if ETRAX_ARCH_V10
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_DMA5_IN
depends on ETRAX_ARCH_V10
bool "DMA 5"
endchoice
choice
prompt "Ser3 DMA out channel"
depends on ETRAX_SERIAL_PORT3
default ETRAX_SERIAL_PORT3_NO_DMA_OUT if ETRAX_ARCH_V32
default ETRAX_SERIAL_PORT3_DMA4_OUT if ETRAX_ARCH_V10
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_DMA4_OUT
depends on ETRAX_ARCH_V10
bool "DMA 4"
endchoice
endmenu
source "drivers/Kconfig"
source "fs/Kconfig"
source "arch/cris/Kconfig.debug"
source "security/Kconfig"
source "crypto/Kconfig"
source "lib/Kconfig"

View File

@ -1,41 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
menu "Kernel hacking"
config PROFILING
bool "Kernel profiling support"
config SYSTEM_PROFILER
bool "System profiling support"
source "lib/Kconfig.debug"
config ETRAX_KGDB
bool "Use kernel GDB debugger"
depends on DEBUG_KERNEL
---help---
The CRIS version of gdb can be used to remotely debug a running
Linux kernel via the serial debug port. Provided you have gdb-cris
installed, run gdb-cris vmlinux, then type
(gdb) set remotebaud 115200 <- kgdb uses 115200 as default
(gdb) target remote /dev/ttyS0 <- maybe you use another port
This should connect you to your booted kernel (or boot it now if you
didn't before). The kernel halts when it boots, waiting for gdb if
this option is turned on!
config DEBUG_NMI_OOPS
bool "NMI causes oops printout"
depends on DEBUG_KERNEL
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.
config NO_SEGFAULT_TERMINATION
bool "Keep segfaulting processes"
help
Place segfaulting user mode processes on a wait queue instead of
delivering a terminating SIGSEGV to allow debugging with gdb.
endmenu

View File

@ -1,104 +0,0 @@
#
# cris/Makefile
#
# This file is included by the global makefile so that you can add your own
# architecture-specific flags and dependencies. Remember to do have actions
# for "archclean" and "archdep" for cleaning up and making dependencies for
# this architecture
#
# This file is subject to the terms and conditions of the GNU General Public
# License. See the file "COPYING" in the main directory of this archive
# for more details.
KBUILD_DEFCONFIG := etrax-100lx_v2_defconfig
arch-y := v10
arch-$(CONFIG_ETRAX_ARCH_V10) := v10
arch-$(CONFIG_ETRAX_ARCH_V32) := v32
# No config available for make clean etc
mach-y := fs
mach-$(CONFIG_CRIS_MACH_ARTPEC3) := a3
mach-$(CONFIG_ETRAXFS) := fs
ifneq ($(arch-y),)
SARCH := arch-$(arch-y)
inc := -Iarch/cris/include/uapi/$(SARCH)
inc += -Iarch/cris/include/$(SARCH)
inc += -Iarch/cris/include/uapi/$(SARCH)/arch
inc += -Iarch/cris/include/$(SARCH)/arch
else
SARCH :=
inc :=
endif
ifneq ($(mach-y),)
MACH := mach-$(mach-y)
inc += -Iarch/cris/include/$(SARCH)/$(MACH)/
inc += -Iarch/cris/include/$(SARCH)/$(MACH)/mach
else
MACH :=
endif
ifneq ($(CONFIG_BUILTIN_DTB),"")
core-$(CONFIG_OF) += arch/cris/boot/dts/
endif
LD = $(CROSS_COMPILE)ld -mcrislinux
OBJCOPYFLAGS := -O binary -R .note -R .comment -S
KBUILD_AFLAGS += -mlinux -march=$(arch-y) $(inc)
KBUILD_CFLAGS += -mlinux -march=$(arch-y) -pipe $(inc)
KBUILD_CPPFLAGS += $(inc)
ifdef CONFIG_FRAME_POINTER
KBUILD_CFLAGS := $(subst -fomit-frame-pointer,,$(KBUILD_CFLAGS)) -g
KBUILD_CFLAGS += -fno-omit-frame-pointer
endif
head-y := arch/cris/$(SARCH)/kernel/head.o
LIBGCC = $(shell $(CC) $(KBUILD_CFLAGS) -print-file-name=libgcc.a)
core-y += arch/cris/kernel/ arch/cris/mm/
core-y += arch/cris/$(SARCH)/kernel/ arch/cris/$(SARCH)/mm/
ifdef CONFIG_ETRAX_ARCH_V32
core-y += arch/cris/$(SARCH)/$(MACH)/
endif
drivers-y += arch/cris/$(SARCH)/drivers/
libs-y += arch/cris/$(SARCH)/lib/ $(LIBGCC)
# cris source path
SRC_ARCH = $(srctree)/arch/cris
# cris object files path
OBJ_ARCH = $(objtree)/arch/cris
boot := arch/cris/boot
MACHINE := arch/cris/$(SARCH)
all: zImage
zImage Image: vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
archprepare:
archclean:
$(Q)if [ -e arch/cris/boot ]; then \
$(MAKE) $(clean)=arch/cris/boot; \
fi
CLEAN_FILES += \
$(boot)/zImage \
$(boot)/compressed/decompress.bin \
$(boot)/compressed/piggy.gz \
$(boot)/rescue/rescue.bin
# MRPROPER_FILES +=
define archhelp
echo '* zImage - Compressed kernel image (arch/cris/boot/zImage)'
echo '* Image - Uncompressed kernel image (arch/cris/boot/Image)'
endef

View File

@ -1,399 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
if ETRAX_ARCH_V10
menu "CRIS v10 options"
# ETRAX 100LX v1 has a MMU "feature" requiring a low mapping
config CRIS_LOW_MAP
bool
depends on ETRAX_ARCH_V10 && ETRAX100LX
default y
config ETRAX_DRAM_VIRTUAL_BASE
hex
depends on ETRAX_ARCH_V10
default "c0000000" if !ETRAX100LX
default "60000000" if ETRAX100LX
choice
prompt "Product LED port"
depends on ETRAX_ARCH_V10
default ETRAX_PA_LEDS
config ETRAX_PA_LEDS
bool "Port-PA-LEDs"
help
The ETRAX network driver is responsible for flashing LED's when
packets arrive and are sent. It uses macros defined in
<file:arch/cris/include/asm/io.h>, and those macros are defined after
what YOU choose in this option. The actual bits used are configured
separately. Select this if the LEDs are on port PA. Some products
put the leds on PB or a memory-mapped latch (CSP0) instead.
config ETRAX_PB_LEDS
bool "Port-PB-LEDs"
help
The ETRAX network driver is responsible for flashing LED's when
packets arrive and are sent. It uses macros defined in
<file:arch/cris/include/asm/io.h>, and those macros are defined after
what YOU choose in this option. The actual bits used are configured
separately. Select this if the LEDs are on port PB. Some products
put the leds on PA or a memory-mapped latch (CSP0) instead.
config ETRAX_CSP0_LEDS
bool "Port-CSP0-LEDs"
help
The ETRAX network driver is responsible for flashing LED's when
packets arrive and are sent. It uses macros defined in
<file:arch/cris/include/asm/io.h>, and those macros are defined after
what YOU choose in this option. The actual bits used are configured
separately. Select this if the LEDs are on a memory-mapped latch
using chip select CSP0, this is mapped at 0x90000000.
Some products put the leds on PA or PB instead.
config ETRAX_NO_LEDS
bool "None"
help
Select this option if you don't have any LED at all.
endchoice
config ETRAX_LED1G
int "First green LED bit"
depends on ETRAX_ARCH_V10 && !ETRAX_NO_LEDS
default "2"
help
Bit to use for the first green LED.
Most Axis products use bit 2 here.
config ETRAX_LED1R
int "First red LED bit"
depends on ETRAX_ARCH_V10 && !ETRAX_NO_LEDS
default "3"
help
Bit to use for the first red LED.
Most Axis products use bit 3 here.
For products with only one controllable LED,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED2G
int "Second green LED bit"
depends on ETRAX_ARCH_V10 && !ETRAX_NO_LEDS
default "4"
help
Bit to use for the second green LED. The "Active" LED.
Most Axis products use bit 4 here.
For products with only one controllable LED,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED2R
int "Second red LED bit"
depends on ETRAX_ARCH_V10 && !ETRAX_NO_LEDS
default "5"
help
Bit to use for the second red LED.
Most Axis products use bit 5 here.
For products with only one controllable LED,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED3G
int "Third green LED bit"
depends on ETRAX_ARCH_V10 && !ETRAX_NO_LEDS
default "2"
help
Bit to use for the third green LED. The "Drive" LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED3R
int "Third red LED bit"
depends on ETRAX_ARCH_V10 && !ETRAX_NO_LEDS
default "2"
help
Bit to use for the third red LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED4R
int "Fourth red LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the fourth red LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED4G
int "Fourth green LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the fourth green LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED5R
int "Fifth red LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the fifth red LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED5G
int "Fifth green LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the fifth green LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED6R
int "Sixth red LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the sixth red LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED6G
int "Sixth green LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the sixth green LED. The "Drive" LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED7R
int "Seventh red LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the seventh red LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED7G
int "Seventh green LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the seventh green LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED8Y
int "Eighth yellow LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the eighth yellow LED. The "Drive" LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED9Y
int "Ninth yellow LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the ninth yellow LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED10Y
int "Tenth yellow LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the tenth yellow LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED11Y
int "Eleventh yellow LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the eleventh yellow LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
config ETRAX_LED12R
int "Twelfth red LED bit"
depends on ETRAX_CSP0_LEDS
default "2"
help
Bit to use for the twelfth red LED.
For products with only one or two controllable LEDs,
set this to same as CONFIG_ETRAX_LED1G (normally 2).
choice
prompt "Product rescue-port"
depends on ETRAX_ARCH_V10
default ETRAX_RESCUE_SER0
config ETRAX_RESCUE_SER0
bool "Serial-0"
help
Select one of the four serial ports as a rescue port. The default
is port 0.
config ETRAX_RESCUE_SER1
bool "Serial-1"
help
Use serial port 1 as the rescue port.
config ETRAX_RESCUE_SER2
bool "Serial-2"
help
Use serial port 2 as the rescue port.
config ETRAX_RESCUE_SER3
bool "Serial-3"
help
Use serial port 3 as the rescue port.
endchoice
config ETRAX_DEF_R_WAITSTATES
hex "R_WAITSTATES"
depends on ETRAX_ARCH_V10
default "95a6"
help
Waitstates for SRAM, Flash and peripherals (not DRAM). 95f8 is a
good choice for most Axis products...
config ETRAX_DEF_R_BUS_CONFIG
hex "R_BUS_CONFIG"
depends on ETRAX_ARCH_V10
default "104"
help
Assorted bits controlling write mode, DMA burst length etc. 104 is
a good choice for most Axis products...
config ETRAX_SDRAM
bool "SDRAM support"
depends on ETRAX_ARCH_V10
help
Enable this if you use SDRAM chips and configure
R_SDRAM_CONFIG and R_SDRAM_TIMING as well.
config ETRAX_DEF_R_DRAM_CONFIG
hex "R_DRAM_CONFIG"
depends on ETRAX_ARCH_V10 && !ETRAX_SDRAM
default "1a200040"
help
The R_DRAM_CONFIG register specifies everything on how the DRAM
chips in the system are connected to the ETRAX CPU. This is
different depending on the manufacturer, chip type and number of
chips. So this value often needs to be different for each Axis
product.
config ETRAX_DEF_R_DRAM_TIMING
hex "R_DRAM_TIMING"
depends on ETRAX_ARCH_V10 && !ETRAX_SDRAM
default "5611"
help
Different DRAM chips have different speeds. Current Axis products
use 50ns DRAM chips which can use the timing: 5611.
config ETRAX_DEF_R_SDRAM_CONFIG
hex "R_SDRAM_CONFIG"
depends on ETRAX_ARCH_V10 && ETRAX_SDRAM
default "d2fa7878"
help
The R_SDRAM_CONFIG register specifies everything on how the SDRAM
chips in the system are connected to the ETRAX CPU. This is
different depending on the manufacturer, chip type and number of
chips. So this value often needs to be different for each Axis
product.
config ETRAX_DEF_R_SDRAM_TIMING
hex "R_SDRAM_TIMING"
depends on ETRAX_ARCH_V10 && ETRAX_SDRAM
default "80004801"
help
Different SDRAM chips have different timing.
config ETRAX_DEF_R_PORT_PA_DIR
hex "R_PORT_PA_DIR"
depends on ETRAX_ARCH_V10
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_R_PORT_PA_DATA
hex "R_PORT_PA_DATA"
depends on ETRAX_ARCH_V10
default "00"
help
Configures the initial data for the general port A bits. Most
products should use 00 here.
config ETRAX_DEF_R_PORT_PB_CONFIG
hex "R_PORT_PB_CONFIG"
depends on ETRAX_ARCH_V10
default "00"
help
Configures the type of the general port B bits. 1 is chip select,
0 is port. Most products should use 00 here.
config ETRAX_DEF_R_PORT_PB_DIR
hex "R_PORT_PB_DIR"
depends on ETRAX_ARCH_V10
default "00"
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. Bits
0 and 1 on port PB are usually used for I2C communication, but the
kernel I2C driver sets the appropriate directions itself so you
don't need to take that into consideration when setting this option.
If you don't know what to use, it is always safe to put all as
inputs.
config ETRAX_DEF_R_PORT_PB_DATA
hex "R_PORT_PB_DATA"
depends on ETRAX_ARCH_V10
default "ff"
help
Configures the initial data for the general port A bits. Most
products should use FF here.
config ETRAX_SOFT_SHUTDOWN
bool "Software Shutdown Support"
depends on ETRAX_ARCH_V10
help
Enable this if ETRAX is used with a power-supply that can be turned
off and on with PS_ON signal. Gives the possibility to detect
powerbutton and then do a power off after unmounting disks.
config ETRAX_SHUTDOWN_BIT
int "Shutdown bit on port CSP0"
depends on ETRAX_SOFT_SHUTDOWN
default "12"
help
Configure what pin on CSPO-port that is used for controlling power
supply.
config ETRAX_POWERBUTTON_BIT
int "Power button bit on port G"
depends on ETRAX_SOFT_SHUTDOWN
default "25"
help
Configure where power button is connected.
endmenu
endif

View File

@ -1,244 +0,0 @@
Memory management for CRIS/MMU
------------------------------
HISTORY:
$Log: README.mm,v $
Revision 1.1 2001/12/17 13:59:27 bjornw
Initial revision
Revision 1.1 2000/07/10 16:25:21 bjornw
Initial revision
Revision 1.4 2000/01/17 02:31:59 bjornw
Added discussion of paging and VM.
Revision 1.3 1999/12/03 16:43:23 hp
Blurb about that the 3.5G-limitation is not a MMU limitation
Revision 1.2 1999/12/03 16:04:21 hp
Picky comment about not mapping the first page
Revision 1.1 1999/12/03 15:41:30 bjornw
First version of CRIS/MMU memory layout specification.
------------------------------
See the ETRAX-NG HSDD for reference.
We use the page-size of 8 kbytes, as opposed to the i386 page-size of 4 kbytes.
The MMU can, apart from the normal mapping of pages, also do a top-level
segmentation of the kernel memory space. We use this feature to avoid having
to use page-tables to map the physical memory into the kernel's address
space. We also use it to keep the user-mode virtual mapping in the same
map during kernel-mode, so that the kernel easily can access the corresponding
user-mode process' data.
As a comparison, the Linux/i386 2.0 puts the kernel and physical RAM at
address 0, overlapping with the user-mode virtual space, so that descriptor
registers are needed for each memory access to specify which MMU space to
map through. That changed in 2.2, putting the kernel/physical RAM at
0xc0000000, to co-exist with the user-mode mapping. We will do something
quite similar, but with the additional complexity of having to map the
internal chip I/O registers and the flash memory area (including SRAM
and peripherial chip-selets).
The kernel-mode segmentation map:
------------------------ ------------------------
FFFFFFFF| | => cached | |
| kernel seg_f | flash | |
F0000000|______________________| | |
EFFFFFFF| | => uncached | |
| kernel seg_e | flash | |
E0000000|______________________| | DRAM |
DFFFFFFF| | paged to any | Un-cached |
| kernel seg_d | =======> | |
D0000000|______________________| | |
CFFFFFFF| | | |
| kernel seg_c |==\ | |
C0000000|______________________| \ |______________________|
BFFFFFFF| | uncached | |
| kernel seg_b |=====\=========>| Registers |
B0000000|______________________| \c |______________________|
AFFFFFFF| | \a | |
| | \c | FLASH/SRAM/Peripheral|
| | \h |______________________|
| | \e | |
| | \d | |
| kernel seg_0 - seg_a | \==>| DRAM |
| | | Cached |
| | paged to any | |
| | =======> |______________________|
| | | |
| | | Illegal |
| | |______________________|
| | | |
| | | FLASH/SRAM/Peripheral|
00000000|______________________| |______________________|
In user-mode it looks the same except that only the space 0-AFFFFFFF is
available. Therefore, in this model, the virtual address space per process
is limited to 0xb0000000 bytes (minus 8192 bytes, since the first page,
0..8191, is never mapped, in order to trap NULL references).
It also means that the total physical RAM that can be mapped is 256 MB
(kseg_c above). More RAM can be mapped by choosing a different segmentation
and shrinking the user-mode memory space.
The MMU can map all 4 GB in user mode, but doing that would mean that a
few extra instructions would be needed for each access to user mode
memory.
The kernel needs access to both cached and uncached flash. Uncached is
necessary because of the special write/erase sequences. Also, the
peripherial chip-selects are decoded from that region.
The kernel also needs its own virtual memory space. That is kseg_d. It
is used by the vmalloc() kernel function to allocate virtual contiguous
chunks of memory not possible using the normal kmalloc physical RAM
allocator.
The setting of the actual MMU control registers to use this layout would
be something like this:
R_MMU_KSEG = ( ( seg_f, seg ) | // Flash cached
( seg_e, seg ) | // Flash uncached
( seg_d, page ) | // kernel vmalloc area
( seg_c, seg ) | // kernel linear segment
( seg_b, seg ) | // kernel linear segment
( seg_a, page ) |
( seg_9, page ) |
( seg_8, page ) |
( seg_7, page ) |
( seg_6, page ) |
( seg_5, page ) |
( seg_4, page ) |
( seg_3, page ) |
( seg_2, page ) |
( seg_1, page ) |
( seg_0, page ) );
R_MMU_KBASE_HI = ( ( base_f, 0x0 ) | // flash/sram/periph cached
( base_e, 0x8 ) | // flash/sram/periph uncached
( base_d, 0x0 ) | // don't care
( base_c, 0x4 ) | // physical RAM cached area
( base_b, 0xb ) | // uncached on-chip registers
( base_a, 0x0 ) | // don't care
( base_9, 0x0 ) | // don't care
( base_8, 0x0 ) ); // don't care
R_MMU_KBASE_LO = ( ( base_7, 0x0 ) | // don't care
( base_6, 0x0 ) | // don't care
( base_5, 0x0 ) | // don't care
( base_4, 0x0 ) | // don't care
( base_3, 0x0 ) | // don't care
( base_2, 0x0 ) | // don't care
( base_1, 0x0 ) | // don't care
( base_0, 0x0 ) ); // don't care
NOTE: while setting up the MMU, we run in a non-mapped mode in the DRAM (0x40
segment) and need to setup the seg_4 to a unity mapping, so that we don't get
a fault before we have had time to jump into the real kernel segment (0xc0). This
is done in head.S temporarily, but fixed by the kernel later in paging_init.
Paging - PTE's, PMD's and PGD's
-------------------------------
[ References: asm/pgtable.h, asm/page.h, asm/mmu.h ]
The paging mechanism uses virtual addresses to split a process memory-space into
pages, a page being the smallest unit that can be freely remapped in memory. On
Linux/CRIS, a page is 8192 bytes (for technical reasons not equal to 4096 as in
most other 32-bit architectures). It would be inefficient to let a virtual memory
mapping be controlled by a long table of page mappings, so it is broken down into
a 2-level structure with a Page Directory containing pointers to Page Tables which
each have maps of up to 2048 pages (8192 / sizeof(void *)). Linux can actually
handle 3-level structures as well, with a Page Middle Directory in between, but
in many cases, this is folded into a two-level structure by excluding the Middle
Directory.
We'll take a look at how an address is translated while we discuss how it's handled
in the Linux kernel.
The example address is 0xd004000c; in binary this is:
31 23 15 7 0
11010000 00000100 00000000 00001100
|______| |__________||____________|
PGD PTE page offset
Given the top-level Page Directory, the offset in that directory is calculated
using the upper 8 bits:
static inline pgd_t * pgd_offset(struct mm_struct * mm, unsigned long address)
{
return mm->pgd + (address >> PGDIR_SHIFT);
}
PGDIR_SHIFT is the log2 of the amount of memory an entry in the PGD can map; in our
case it is 24, corresponding to 16 MB. This means that each entry in the PGD
corresponds to 16 MB of virtual memory.
The pgd_t from our example will therefore be the 208'th (0xd0) entry in mm->pgd.
Since the Middle Directory does not exist, it is a unity mapping:
static inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address)
{
return (pmd_t *) dir;
}
The Page Table provides the final lookup by using bits 13 to 23 as index:
static inline pte_t * pte_offset(pmd_t * dir, unsigned long address)
{
return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) &
(PTRS_PER_PTE - 1));
}
PAGE_SHIFT is the log2 of the size of a page; 13 in our case. PTRS_PER_PTE is
the number of pointers that fit in a Page Table and is used to mask off the
PGD-part of the address.
The so-far unused bits 0 to 12 are used to index inside a page linearily.
The VM system
-------------
The kernels own page-directory is the swapper_pg_dir, cleared in paging_init,
and contains the kernels virtual mappings (the kernel itself is not paged - it
is mapped linearily using kseg_c as described above). Architectures without
kernel segments like the i386, need to setup swapper_pg_dir directly in head.S
to map the kernel itself. swapper_pg_dir is pointed to by init_mm.pgd as the
init-task's PGD.
To see what support functions are used to setup a page-table, let's look at the
kernel's internal paged memory system, vmalloc/vfree.
void * vmalloc(unsigned long size)
The vmalloc-system keeps a paged segment in kernel-space at 0xd0000000. What
happens first is that a virtual address chunk is allocated to the request using
get_vm_area(size). After that, physical RAM pages are allocated and put into
the kernel's page-table using alloc_area_pages(addr, size).
static int alloc_area_pages(unsigned long address, unsigned long size)
First the PGD entry is found using init_mm.pgd. This is passed to
alloc_area_pmd (remember the 3->2 folding). It uses pte_alloc_kernel to
check if the PGD entry points anywhere - if not, a page table page is
allocated and the PGD entry updated. Then the alloc_area_pte function is
used just like alloc_area_pmd to check which page table entry is desired,
and a physical page is allocated and the table entry updated. All of this
is repeated at the top-level until the entire address range specified has
been mapped.

View File

@ -1,561 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
if ETRAX_ARCH_V10
config ETRAX_ETHERNET
bool "Ethernet support"
depends on ETRAX_ARCH_V10 && NETDEVICES
select MII
help
This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet
controller.
config ETRAX_SERIAL
bool "Serial-port support"
depends on ETRAX_ARCH_V10
help
Enables the ETRAX 100 serial driver for ser0 (ttyS0)
You probably want this enabled.
config ETRAX_SERIAL_FAST_TIMER
bool "Use fast timers for serial DMA flush (experimental)"
depends on ETRAX_SERIAL
help
Select this to have the serial DMAs flushed at a higher rate than
normally, possible by using the fast timer API, the timeout is
approx. 4 character times.
If unsure, say N.
config ETRAX_SERIAL_FLUSH_DMA_FAST
bool "Fast serial port DMA flush"
depends on ETRAX_SERIAL && !ETRAX_SERIAL_FAST_TIMER
help
Select this to have the serial DMAs flushed at a higher rate than
normally possible through a fast timer interrupt (currently at
15360 Hz).
If unsure, say N.
config ETRAX_SERIAL_RX_TIMEOUT_TICKS
int "Receive flush timeout (ticks) "
depends on ETRAX_SERIAL && !ETRAX_SERIAL_FAST_TIMER && !ETRAX_SERIAL_FLUSH_DMA_FAST
default "5"
help
Number of timer ticks between flush of receive fifo (1 tick = 10ms).
Try 0-3 for low latency applications. Approx 5 for high load
applications (e.g. PPP). Maybe this should be more adaptive some
day...
config ETRAX_SERIAL_PORT0
bool "Serial port 0 enabled"
depends on ETRAX_SERIAL
help
Enables the ETRAX 100 serial driver for ser0 (ttyS0)
Normally you want this on, unless you use external DMA 1 that uses
the same DMA channels.
choice
prompt "Ser0 DTR, RI, DSR and CD assignment"
depends on ETRAX_SERIAL_PORT0
default ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE
config ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE
bool "No_DTR_RI_DSR_CD"
config ETRAX_SER0_DTR_RI_DSR_CD_ON_PA
bool "DTR_RI_DSR_CD_on_PA"
config ETRAX_SER0_DTR_RI_DSR_CD_ON_PB
bool "DTR_RI_DSR_CD_on_PB"
help
Enables the status and control signals DTR, RI, DSR and CD on PB for
ser0.
config ETRAX_SER0_DTR_RI_DSR_CD_MIXED
bool "DTR_RI_DSR_CD_mixed_on_PA_and_PB"
endchoice
config ETRAX_SER0_DTR_ON_PA_BIT
int "Ser0 DTR on PA bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT0
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
default "4" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
config ETRAX_SER0_RI_ON_PA_BIT
int "Ser0 RI on PA bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT0
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
default "5" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
config ETRAX_SER0_DSR_ON_PA_BIT
int "Ser0 DSR on PA bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT0
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
default "6" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
config ETRAX_SER0_CD_ON_PA_BIT
int "Ser0 CD on PA bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT0
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
default "7" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
config ETRAX_SER0_DTR_ON_PB_BIT
int "Ser0 DTR on PB bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT0
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
default "4" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
help
Specify the pin of the PB port to carry the DTR signal for serial
port 0.
config ETRAX_SER0_RI_ON_PB_BIT
int "Ser0 RI on PB bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT0
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
default "5" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
help
Specify the pin of the PB port to carry the RI signal for serial
port 0.
config ETRAX_SER0_DSR_ON_PB_BIT
int "Ser0 DSR on PB bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT0
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
default "6" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
help
Specify the pin of the PB port to carry the DSR signal for serial
port 0.
config ETRAX_SER0_CD_ON_PB_BIT
int "Ser0 CD on PB bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT0
default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED
default "7" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED
help
Specify the pin of the PB port to carry the CD signal for serial
port 0.
config ETRAX_SERIAL_PORT1
bool "Serial port 1 enabled"
depends on ETRAX_SERIAL
help
Enables the ETRAX 100 serial driver for ser1 (ttyS1).
choice
prompt "Ser1 DTR, RI, DSR and CD assignment"
depends on ETRAX_SERIAL_PORT1
default ETRAX_SER1_DTR_RI_DSR_CD_ON_NONE
config ETRAX_SER1_DTR_RI_DSR_CD_ON_NONE
bool "No_DTR_RI_DSR_CD"
config ETRAX_SER1_DTR_RI_DSR_CD_ON_PA
bool "DTR_RI_DSR_CD_on_PA"
config ETRAX_SER1_DTR_RI_DSR_CD_ON_PB
bool "DTR_RI_DSR_CD_on_PB"
help
Enables the status and control signals DTR, RI, DSR and CD on PB for
ser1.
config ETRAX_SER1_DTR_RI_DSR_CD_MIXED
bool "DTR_RI_DSR_CD_mixed_on_PA_and_PB"
endchoice
config ETRAX_SER1_DTR_ON_PA_BIT
int "Ser1 DTR on PA bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT1
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
default "4" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
config ETRAX_SER1_RI_ON_PA_BIT
int "Ser1 RI on PA bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT1
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
default "5" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
config ETRAX_SER1_DSR_ON_PA_BIT
int "Ser1 DSR on PA bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT1
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
default "6" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
config ETRAX_SER1_CD_ON_PA_BIT
int "Ser1 CD on PA bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT1
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
default "7" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
config ETRAX_SER1_DTR_ON_PB_BIT
int "Ser1 DTR on PB bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT1
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
default "4" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
help
Specify the pin of the PB port to carry the DTR signal for serial
port 1.
config ETRAX_SER1_RI_ON_PB_BIT
int "Ser1 RI on PB bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT1
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
default "5" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
help
Specify the pin of the PB port to carry the RI signal for serial
port 1.
config ETRAX_SER1_DSR_ON_PB_BIT
int "Ser1 DSR on PB bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT1
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
default "6" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
help
Specify the pin of the PB port to carry the DSR signal for serial
port 1.
config ETRAX_SER1_CD_ON_PB_BIT
int "Ser1 CD on PB bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT1
default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED
default "7" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED
help
Specify the pin of the PB port to carry the CD signal for serial
port 1.
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
bool "Serial port 2 enabled"
depends on ETRAX_SERIAL
help
Enables the ETRAX 100 serial driver for ser2 (ttyS2).
choice
prompt "Ser2 DTR, RI, DSR and CD assignment"
depends on ETRAX_SERIAL_PORT2
default ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE
config ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE
bool "No_DTR_RI_DSR_CD"
config ETRAX_SER2_DTR_RI_DSR_CD_ON_PA
bool "DTR_RI_DSR_CD_on_PA"
help
Enables the status and control signals DTR, RI, DSR and CD on PA for
ser2.
config ETRAX_SER2_DTR_RI_DSR_CD_ON_PB
bool "DTR_RI_DSR_CD_on_PB"
config ETRAX_SER2_DTR_RI_DSR_CD_MIXED
bool "DTR_RI_DSR_CD_mixed_on_PA_and_PB"
endchoice
config ETRAX_SER2_DTR_ON_PA_BIT
int "Ser2 DTR on PA bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT2
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
default "4" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
help
Specify the pin of the PA port to carry the DTR signal for serial
port 2.
config ETRAX_SER2_RI_ON_PA_BIT
int "Ser2 RI on PA bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT2
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
default "5" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
help
Specify the pin of the PA port to carry the RI signal for serial
port 2.
config ETRAX_SER2_DSR_ON_PA_BIT
int "Ser2 DSR on PA bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT2
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
default "6" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
help
Specify the pin of the PA port to carry the DTR signal for serial
port 2.
config ETRAX_SER2_CD_ON_PA_BIT
int "Ser2 CD on PA bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT2
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
default "7" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
help
Specify the pin of the PA port to carry the CD signal for serial
port 2.
config ETRAX_SER2_DTR_ON_PB_BIT
int "Ser2 DTR on PB bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT2
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
default "4" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
config ETRAX_SER2_RI_ON_PB_BIT
int "Ser2 RI on PB bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT2
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
default "5" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
config ETRAX_SER2_DSR_ON_PB_BIT
int "Ser2 DSR on PB bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT2
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
default "6" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
config ETRAX_SER2_CD_ON_PB_BIT
int "Ser2 CD on PB bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT2
default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED
default "7" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED
config ETRAX_SERIAL_PORT3
bool "Serial port 3 enabled"
depends on ETRAX_SERIAL
help
Enables the ETRAX 100 serial driver for ser3 (ttyS3).
choice
prompt "Ser3 DTR, RI, DSR and CD assignment"
depends on ETRAX_SERIAL_PORT3
default ETRAX_SER3_DTR_RI_DSR_CD_ON_NONE
config ETRAX_SER3_DTR_RI_DSR_CD_ON_NONE
bool "No_DTR_RI_DSR_CD"
config ETRAX_SER3_DTR_RI_DSR_CD_ON_PA
bool "DTR_RI_DSR_CD_on_PA"
config ETRAX_SER3_DTR_RI_DSR_CD_ON_PB
bool "DTR_RI_DSR_CD_on_PB"
config ETRAX_SER3_DTR_RI_DSR_CD_MIXED
bool "DTR_RI_DSR_CD_mixed_on_PA_and_PB"
endchoice
config ETRAX_SER3_DTR_ON_PA_BIT
int "Ser3 DTR on PA bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PA || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT3
default "-1"
config ETRAX_SER3_RI_ON_PA_BIT
int "Ser3 RI on PA bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PA || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT3
default "-1"
config ETRAX_SER3_DSR_ON_PA_BIT
int "Ser3 DSR on PA bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PA || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT3
default "-1"
config ETRAX_SER3_CD_ON_PA_BIT
int "Ser3 CD on PA bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PA || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT3
default "-1"
config ETRAX_SER3_DTR_ON_PB_BIT
int "Ser3 DTR on PB bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PB || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT3
default "-1"
config ETRAX_SER3_RI_ON_PB_BIT
int "Ser3 RI on PB bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PB || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT3
default "-1"
config ETRAX_SER3_DSR_ON_PB_BIT
int "Ser3 DSR on PB bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PB || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT3
default "-1"
config ETRAX_SER3_CD_ON_PB_BIT
int "Ser3 CD on PB bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PB || ETRAX_SER3_DTR_RI_DSR_CD_MIXED
depends on ETRAX_SERIAL_PORT3
default "-1"
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://en.wikipedia.org/wiki/Rs485>
config ETRAX_RS485_ON_PA
bool "RS-485 mode on PA"
depends on ETRAX_RS485
help
Control Driver Output Enable on RS485 transceiver using a pin on PA
port:
Axis 2400/2401 uses PA 3.
config ETRAX_RS485_ON_PA_BIT
int "RS-485 mode on PA bit"
depends on ETRAX_RS485_ON_PA
default "3"
help
Control Driver Output Enable on RS485 transceiver using a this bit
on PA port.
config ETRAX_RS485_DISABLE_RECEIVER
bool "Disable serial receiver"
depends on ETRAX_RS485
help
It's 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_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 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_I2C
bool "I2C support"
depends on ETRAX_ARCH_V10
help
Enables an I2C driver on ETRAX100.
EXAMPLE usage:
i2c_arg = I2C_WRITEARG(STA013_WRITE_ADDR, reg, val);
ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_WRITEREG), i2c_arg);
i2c_arg = I2C_READARG(STA013_READ_ADDR, reg);
val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg);
# this is true for most products since PB-I2C seems to be somewhat
# flawed..
config ETRAX_I2C_USES_PB_NOT_PB_I2C
bool "I2C uses PB not PB-I2C"
depends on ETRAX_I2C
help
Select whether to use the special I2C mode in the PB I/O register or
not. This option needs to be selected in order to use some drivers
that access the I2C I/O pins directly instead of going through the
I2C driver, like the DS1302 realtime-clock driver. If you are
uncertain, choose Y here.
config ETRAX_I2C_DATA_PORT
int "I2C SDA bit number"
depends on ETRAX_I2C_USES_PB_NOT_PB_I2C
default "0"
help
Selects the pin on Port B where the data pin is connected
config ETRAX_I2C_CLK_PORT
int "I2C SCL bit number"
depends on ETRAX_I2C_USES_PB_NOT_PB_I2C
default "1"
help
Select the pin on Port B where the clock pin is connected
config ETRAX_I2C_EEPROM
bool "I2C EEPROM (non-volatile RAM) support"
depends on ETRAX_I2C
help
Enables I2C EEPROM (non-volatile RAM) on PB0 and PB1 using the I2C
driver. Select size option: Probed, 2k, 8k, 16k.
(Probing works for 2k and 8k but not that well for 16k)
choice
prompt "EEPROM size"
depends on ETRAX_I2C_EEPROM
default ETRAX_I2C_EEPROM_PROBE
config ETRAX_I2C_EEPROM_PROBE
bool "Probed"
help
Specifies size or auto probe of the EEPROM size.
Options: Probed, 2k, 8k, 16k.
(Probing works for 2k and 8k but not that well for 16k)
config ETRAX_I2C_EEPROM_2KB
bool "2kB"
help
Use a 2kB EEPROM.
config ETRAX_I2C_EEPROM_8KB
bool "8kB"
help
Use a 8kB EEPROM.
config ETRAX_I2C_EEPROM_16KB
bool "16kB"
help
Use a 16kB EEPROM.
endchoice
config ETRAX_GPIO
bool "GPIO support"
depends on ETRAX_ARCH_V10
---help---
Enables the ETRAX general port device (major 120, minors 0 and 1).
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);
val = ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_READBITS), NULL);
Remember that you need to setup the port directions appropriately in
the General configuration.
config ETRAX_PA_CHANGEABLE_DIR
hex "PA user changeable dir mask"
depends on ETRAX_GPIO
default "00"
help
This is a bitmask with information of what bits in PA that a user
can change direction on using ioctl's.
Bit set = changeable.
You probably want 00 here.
config ETRAX_PA_CHANGEABLE_BITS
hex "PA user changeable bits mask"
depends on ETRAX_GPIO
default "FF"
help
This is a bitmask with information of what bits in PA that a user
can change the value on using ioctl's.
Bit set = changeable.
You probably want 00 here.
config ETRAX_PB_CHANGEABLE_DIR
hex "PB user changeable dir mask"
depends on ETRAX_GPIO
default "00"
help
This is a bitmask with information of what bits in PB that a user
can change direction on using ioctl's.
Bit set = changeable.
You probably want 00 here.
config ETRAX_PB_CHANGEABLE_BITS
hex "PB user changeable bits mask"
depends on ETRAX_GPIO
default "FF"
help
This is a bitmask with information of what bits in PB that a user
can change the value on using ioctl's.
Bit set = changeable.
You probably want 00 here.
endif

View File

@ -1,11 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for Etrax-specific drivers
#
obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o
obj-$(CONFIG_ETRAX_I2C) += i2c.o
obj-$(CONFIG_ETRAX_I2C_EEPROM) += eeprom.o
obj-$(CONFIG_ETRAX_GPIO) += gpio.o
obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o

View File

@ -1,413 +0,0 @@
/*
* Physical mapping layer for MTD using the Axis partitiontable format
*
* Copyright (c) 2001, 2002 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.
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.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/axisflashmap.h>
#include <asm/mmu.h>
#include <arch/sv_addr_ag.h>
#ifdef CONFIG_CRIS_LOW_MAP
#define FLASH_UNCACHED_ADDR KSEG_8
#define FLASH_CACHED_ADDR KSEG_5
#else
#define FLASH_UNCACHED_ADDR KSEG_E
#define FLASH_CACHED_ADDR KSEG_F
#endif
#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 __u32
#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_CFI
mtd_cs = do_map_probe("cfi_probe", map_cs);
#endif
#ifdef CONFIG_MTD_JEDECPROBE
if (!mtd_cs)
mtd_cs = do_map_probe("jedec_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 boundaries.
*
* 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.
*/
static struct mtd_info *flash_probe(void)
{
struct mtd_info *mtd_cse0;
struct mtd_info *mtd_cse1;
struct mtd_info *mtd_cse;
mtd_cse0 = probe_cs(&map_cse0);
mtd_cse1 = probe_cs(&map_cse1);
if (!mtd_cse0 && !mtd_cse1) {
/* No chip found. */
return NULL;
}
if (mtd_cse0 && mtd_cse1) {
struct mtd_info *mtds[] = { mtd_cse0, mtd_cse1 };
/* 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_cse = mtd_concat_create(mtds, ARRAY_SIZE(mtds),
"cse0+cse1");
if (!mtd_cse) {
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_cse = mtd_cse0;
map_destroy(mtd_cse1);
}
} else {
mtd_cse = mtd_cse0? mtd_cse0 : mtd_cse1;
}
return mtd_cse;
}
/*
* 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[] = " /dev/flash%d at 0x%08x, size 0x%08x\n";
if (!(mymtd = flash_probe())) {
/* 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;
ptable_head = (struct partitiontable_head *)(FLASH_CACHED_ADDR +
CONFIG_ETRAX_PTABLE_SECTOR +
PARTITION_TABLE_OFFSET);
}
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;
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";
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 = mtd_device_register(mymtd,
axis_default_partitions,
NUM_DEFAULT_PARTITIONS);
} else {
err = mtd_device_register(mymtd, axis_partitions,
pidx);
}
if (err)
panic("axisflashmap could not add MTD partitions!\n");
}
if (!romfs_in_flash) {
/* Create an RAM device for the root partition (romfs). */
#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 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 = 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, (unsigned)romfs_start,
(unsigned)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);

View File

@ -1,852 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*!*****************************************************************************
*!
*! Implements an interface for i2c compatible eeproms to run under Linux.
*! Supports 2k, 8k(?) and 16k. Uses adaptive timing adjustments by
*! Johan.Adolfsson@axis.com
*!
*! Probing results:
*! 8k or not is detected (the assumes 2k or 16k)
*! 2k or 16k detected using test reads and writes.
*!
*!------------------------------------------------------------------------
*! HISTORY
*!
*! DATE NAME CHANGES
*! ---- ---- -------
*! Aug 28 1999 Edgar Iglesias Initial Version
*! Aug 31 1999 Edgar Iglesias Allow simultaneous users.
*! Sep 03 1999 Edgar Iglesias Updated probe.
*! Sep 03 1999 Edgar Iglesias Added bail-out stuff if we get interrupted
*! in the spin-lock.
*!
*! (c) 1999 Axis Communications AB, Lund, Sweden
*!*****************************************************************************/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include "i2c.h"
#define D(x)
/* If we should use adaptive timing or not: */
/* #define EEPROM_ADAPTIVE_TIMING */
#define EEPROM_MAJOR_NR 122 /* use a LOCAL/EXPERIMENTAL major for now */
#define EEPROM_MINOR_NR 0
/* Empirical sane initial value of the delay, the value will be adapted to
* what the chip needs when using EEPROM_ADAPTIVE_TIMING.
*/
#define INITIAL_WRITEDELAY_US 4000
#define MAX_WRITEDELAY_US 10000 /* 10 ms according to spec for 2KB EEPROM */
/* This one defines how many times to try when eeprom fails. */
#define EEPROM_RETRIES 10
#define EEPROM_2KB (2 * 1024)
/*#define EEPROM_4KB (4 * 1024)*/ /* Exists but not used in Axis products */
#define EEPROM_8KB (8 * 1024 - 1 ) /* Last byte has write protection bit */
#define EEPROM_16KB (16 * 1024)
#define i2c_delay(x) udelay(x)
/*
* This structure describes the attached eeprom chip.
* The values are probed for.
*/
struct eeprom_type
{
unsigned long size;
unsigned long sequential_write_pagesize;
unsigned char select_cmd;
unsigned long usec_delay_writecycles; /* Min time between write cycles
(up to 10ms for some models) */
unsigned long usec_delay_step; /* For adaptive algorithm */
int adapt_state; /* 1 = To high , 0 = Even, -1 = To low */
/* this one is to keep the read/write operations atomic */
struct mutex lock;
int retry_cnt_addr; /* Used to keep track of number of retries for
adaptive timing adjustments */
int retry_cnt_read;
};
static int eeprom_open(struct inode * inode, struct file * file);
static loff_t eeprom_lseek(struct file * file, loff_t offset, int orig);
static ssize_t eeprom_read(struct file * file, char * buf, size_t count,
loff_t *off);
static ssize_t eeprom_write(struct file * file, const char * buf, size_t count,
loff_t *off);
static int eeprom_close(struct inode * inode, struct file * file);
static int eeprom_address(unsigned long addr);
static int read_from_eeprom(char * buf, int count);
static int eeprom_write_buf(loff_t addr, const char * buf, int count);
static int eeprom_read_buf(loff_t addr, char * buf, int count);
static void eeprom_disable_write_protect(void);
static const char eeprom_name[] = "eeprom";
/* chip description */
static struct eeprom_type eeprom;
/* This is the exported file-operations structure for this device. */
const struct file_operations eeprom_fops =
{
.llseek = eeprom_lseek,
.read = eeprom_read,
.write = eeprom_write,
.open = eeprom_open,
.release = eeprom_close
};
/* eeprom init call. Probes for different eeprom models. */
int __init eeprom_init(void)
{
mutex_init(&eeprom.lock);
#ifdef CONFIG_ETRAX_I2C_EEPROM_PROBE
#define EETEXT "Found"
#else
#define EETEXT "Assuming"
#endif
if (register_chrdev(EEPROM_MAJOR_NR, eeprom_name, &eeprom_fops))
{
printk(KERN_INFO "%s: unable to get major %d for eeprom device\n",
eeprom_name, EEPROM_MAJOR_NR);
return -1;
}
printk("EEPROM char device v0.3, (c) 2000 Axis Communications AB\n");
/*
* Note: Most of this probing method was taken from the printserver (5470e)
* codebase. It did not contain a way of finding the 16kB chips
* (M24128 or variants). The method used here might not work
* for all models. If you encounter problems the easiest way
* is probably to define your model within #ifdef's, and hard-
* code it.
*/
eeprom.size = 0;
eeprom.usec_delay_writecycles = INITIAL_WRITEDELAY_US;
eeprom.usec_delay_step = 128;
eeprom.adapt_state = 0;
#ifdef CONFIG_ETRAX_I2C_EEPROM_PROBE
i2c_start();
i2c_outbyte(0x80);
if(!i2c_getack())
{
/* It's not 8k.. */
int success = 0;
unsigned char buf_2k_start[16];
/* Im not sure this will work... :) */
/* assume 2kB, if failure go for 16kB */
/* Test with 16kB settings.. */
/* If it's a 2kB EEPROM and we address it outside it's range
* it will mirror the address space:
* 1. We read two locations (that are mirrored),
* if the content differs * it's a 16kB EEPROM.
* 2. if it doesn't differ - write different value to one of the locations,
* check the other - if content still is the same it's a 2k EEPROM,
* restore original data.
*/
#define LOC1 8
#define LOC2 (0x1fb) /*1fb, 3ed, 5df, 7d1 */
/* 2k settings */
i2c_stop();
eeprom.size = EEPROM_2KB;
eeprom.select_cmd = 0xA0;
eeprom.sequential_write_pagesize = 16;
if( eeprom_read_buf( 0, buf_2k_start, 16 ) == 16 )
{
D(printk("2k start: '%16.16s'\n", buf_2k_start));
}
else
{
printk(KERN_INFO "%s: Failed to read in 2k mode!\n", eeprom_name);
}
/* 16k settings */
eeprom.size = EEPROM_16KB;
eeprom.select_cmd = 0xA0;
eeprom.sequential_write_pagesize = 64;
{
unsigned char loc1[4], loc2[4], tmp[4];
if( eeprom_read_buf(LOC2, loc2, 4) == 4)
{
if( eeprom_read_buf(LOC1, loc1, 4) == 4)
{
D(printk("0 loc1: (%i) '%4.4s' loc2 (%i) '%4.4s'\n",
LOC1, loc1, LOC2, loc2));
#if 0
if (memcmp(loc1, loc2, 4) != 0 )
{
/* It's 16k */
printk(KERN_INFO "%s: 16k detected in step 1\n", eeprom_name);
eeprom.size = EEPROM_16KB;
success = 1;
}
else
#endif
{
/* Do step 2 check */
/* Invert value */
loc1[0] = ~loc1[0];
if (eeprom_write_buf(LOC1, loc1, 1) == 1)
{
/* If 2k EEPROM this write will actually write 10 bytes
* from pos 0
*/
D(printk("1 loc1: (%i) '%4.4s' loc2 (%i) '%4.4s'\n",
LOC1, loc1, LOC2, loc2));
if( eeprom_read_buf(LOC1, tmp, 4) == 4)
{
D(printk("2 loc1: (%i) '%4.4s' tmp '%4.4s'\n",
LOC1, loc1, tmp));
if (memcmp(loc1, tmp, 4) != 0 )
{
printk(KERN_INFO "%s: read and write differs! Not 16kB\n",
eeprom_name);
loc1[0] = ~loc1[0];
if (eeprom_write_buf(LOC1, loc1, 1) == 1)
{
success = 1;
}
else
{
printk(KERN_INFO "%s: Restore 2k failed during probe,"
" EEPROM might be corrupt!\n", eeprom_name);
}
i2c_stop();
/* Go to 2k mode and write original data */
eeprom.size = EEPROM_2KB;
eeprom.select_cmd = 0xA0;
eeprom.sequential_write_pagesize = 16;
if( eeprom_write_buf(0, buf_2k_start, 16) == 16)
{
}
else
{
printk(KERN_INFO "%s: Failed to write back 2k start!\n",
eeprom_name);
}
eeprom.size = EEPROM_2KB;
}
}
if(!success)
{
if( eeprom_read_buf(LOC2, loc2, 1) == 1)
{
D(printk("0 loc1: (%i) '%4.4s' loc2 (%i) '%4.4s'\n",
LOC1, loc1, LOC2, loc2));
if (memcmp(loc1, loc2, 4) == 0 )
{
/* Data the same, must be mirrored -> 2k */
/* Restore data */
printk(KERN_INFO "%s: 2k detected in step 2\n", eeprom_name);
loc1[0] = ~loc1[0];
if (eeprom_write_buf(LOC1, loc1, 1) == 1)
{
success = 1;
}
else
{
printk(KERN_INFO "%s: Restore 2k failed during probe,"
" EEPROM might be corrupt!\n", eeprom_name);
}
eeprom.size = EEPROM_2KB;
}
else
{
printk(KERN_INFO "%s: 16k detected in step 2\n",
eeprom_name);
loc1[0] = ~loc1[0];
/* Data differs, assume 16k */
/* Restore data */
if (eeprom_write_buf(LOC1, loc1, 1) == 1)
{
success = 1;
}
else
{
printk(KERN_INFO "%s: Restore 16k failed during probe,"
" EEPROM might be corrupt!\n", eeprom_name);
}
eeprom.size = EEPROM_16KB;
}
}
}
}
} /* read LOC1 */
} /* address LOC1 */
if (!success)
{
printk(KERN_INFO "%s: Probing failed!, using 2KB!\n", eeprom_name);
eeprom.size = EEPROM_2KB;
}
} /* read */
}
}
else
{
i2c_outbyte(0x00);
if(!i2c_getack())
{
/* No 8k */
eeprom.size = EEPROM_2KB;
}
else
{
i2c_start();
i2c_outbyte(0x81);
if (!i2c_getack())
{
eeprom.size = EEPROM_2KB;
}
else
{
/* It's a 8kB */
i2c_inbyte();
eeprom.size = EEPROM_8KB;
}
}
}
i2c_stop();
#elif defined(CONFIG_ETRAX_I2C_EEPROM_16KB)
eeprom.size = EEPROM_16KB;
#elif defined(CONFIG_ETRAX_I2C_EEPROM_8KB)
eeprom.size = EEPROM_8KB;
#elif defined(CONFIG_ETRAX_I2C_EEPROM_2KB)
eeprom.size = EEPROM_2KB;
#endif
switch(eeprom.size)
{
case (EEPROM_2KB):
printk("%s: " EETEXT " i2c compatible 2kB eeprom.\n", eeprom_name);
eeprom.sequential_write_pagesize = 16;
eeprom.select_cmd = 0xA0;
break;
case (EEPROM_8KB):
printk("%s: " EETEXT " i2c compatible 8kB eeprom.\n", eeprom_name);
eeprom.sequential_write_pagesize = 16;
eeprom.select_cmd = 0x80;
break;
case (EEPROM_16KB):
printk("%s: " EETEXT " i2c compatible 16kB eeprom.\n", eeprom_name);
eeprom.sequential_write_pagesize = 64;
eeprom.select_cmd = 0xA0;
break;
default:
eeprom.size = 0;
printk("%s: Did not find a supported eeprom\n", eeprom_name);
break;
}
eeprom_disable_write_protect();
return 0;
}
/* Opens the device. */
static int eeprom_open(struct inode * inode, struct file * file)
{
if(iminor(inode) != EEPROM_MINOR_NR)
return -ENXIO;
if(imajor(inode) != EEPROM_MAJOR_NR)
return -ENXIO;
if( eeprom.size > 0 )
{
/* OK */
return 0;
}
/* No EEprom found */
return -EFAULT;
}
/* Changes the current file position. */
static loff_t eeprom_lseek(struct file * file, loff_t offset, int orig)
{
/*
* orig 0: position from beginning of eeprom
* orig 1: relative from current position
* orig 2: position from last eeprom address
*/
switch (orig)
{
case 0:
file->f_pos = offset;
break;
case 1:
file->f_pos += offset;
break;
case 2:
file->f_pos = eeprom.size - offset;
break;
default:
return -EINVAL;
}
/* truncate position */
if (file->f_pos < 0)
{
file->f_pos = 0;
return(-EOVERFLOW);
}
if (file->f_pos >= eeprom.size)
{
file->f_pos = eeprom.size - 1;
return(-EOVERFLOW);
}
return ( file->f_pos );
}
/* Reads data from eeprom. */
static int eeprom_read_buf(loff_t addr, char * buf, int count)
{
return eeprom_read(NULL, buf, count, &addr);
}
/* Reads data from eeprom. */
static ssize_t eeprom_read(struct file * file, char * buf, size_t count, loff_t *off)
{
int read=0;
unsigned long p = *off;
unsigned char page;
if(p >= eeprom.size) /* Address i 0 - (size-1) */
{
return -EFAULT;
}
if (mutex_lock_interruptible(&eeprom.lock))
return -EINTR;
page = (unsigned char) (p >> 8);
if(!eeprom_address(p))
{
printk(KERN_INFO "%s: Read failed to address the eeprom: "
"0x%08X (%i) page: %i\n", eeprom_name, (int)p, (int)p, page);
i2c_stop();
/* don't forget to wake them up */
mutex_unlock(&eeprom.lock);
return -EFAULT;
}
if( (p + count) > eeprom.size)
{
/* truncate count */
count = eeprom.size - p;
}
/* stop dummy write op and initiate the read op */
i2c_start();
/* special case for small eeproms */
if(eeprom.size < EEPROM_16KB)
{
i2c_outbyte( eeprom.select_cmd | 1 | (page << 1) );
}
/* go on with the actual read */
read = read_from_eeprom( buf, count);
if(read > 0)
{
*off += read;
}
mutex_unlock(&eeprom.lock);
return read;
}
/* Writes data to eeprom. */
static int eeprom_write_buf(loff_t addr, const char * buf, int count)
{
return eeprom_write(NULL, buf, count, &addr);
}
/* Writes data to eeprom. */
static ssize_t eeprom_write(struct file * file, const char * buf, size_t count,
loff_t *off)
{
int i, written, restart=1;
unsigned long p;
if (!access_ok(VERIFY_READ, buf, count))
{
return -EFAULT;
}
/* bail out if we get interrupted */
if (mutex_lock_interruptible(&eeprom.lock))
return -EINTR;
for(i = 0; (i < EEPROM_RETRIES) && (restart > 0); i++)
{
restart = 0;
written = 0;
p = *off;
while( (written < count) && (p < eeprom.size))
{
/* address the eeprom */
if(!eeprom_address(p))
{
printk(KERN_INFO "%s: Write failed to address the eeprom: "
"0x%08X (%i) \n", eeprom_name, (int)p, (int)p);
i2c_stop();
/* don't forget to wake them up */
mutex_unlock(&eeprom.lock);
return -EFAULT;
}
#ifdef EEPROM_ADAPTIVE_TIMING
/* Adaptive algorithm to adjust timing */
if (eeprom.retry_cnt_addr > 0)
{
/* To Low now */
D(printk(">D=%i d=%i\n",
eeprom.usec_delay_writecycles, eeprom.usec_delay_step));
if (eeprom.usec_delay_step < 4)
{
eeprom.usec_delay_step++;
eeprom.usec_delay_writecycles += eeprom.usec_delay_step;
}
else
{
if (eeprom.adapt_state > 0)
{
/* To Low before */
eeprom.usec_delay_step *= 2;
if (eeprom.usec_delay_step > 2)
{
eeprom.usec_delay_step--;
}
eeprom.usec_delay_writecycles += eeprom.usec_delay_step;
}
else if (eeprom.adapt_state < 0)
{
/* To High before (toggle dir) */
eeprom.usec_delay_writecycles += eeprom.usec_delay_step;
if (eeprom.usec_delay_step > 1)
{
eeprom.usec_delay_step /= 2;
eeprom.usec_delay_step--;
}
}
}
eeprom.adapt_state = 1;
}
else
{
/* To High (or good) now */
D(printk("<D=%i d=%i\n",
eeprom.usec_delay_writecycles, eeprom.usec_delay_step));
if (eeprom.adapt_state < 0)
{
/* To High before */
if (eeprom.usec_delay_step > 1)
{
eeprom.usec_delay_step *= 2;
eeprom.usec_delay_step--;
if (eeprom.usec_delay_writecycles > eeprom.usec_delay_step)
{
eeprom.usec_delay_writecycles -= eeprom.usec_delay_step;
}
}
}
else if (eeprom.adapt_state > 0)
{
/* To Low before (toggle dir) */
if (eeprom.usec_delay_writecycles > eeprom.usec_delay_step)
{
eeprom.usec_delay_writecycles -= eeprom.usec_delay_step;
}
if (eeprom.usec_delay_step > 1)
{
eeprom.usec_delay_step /= 2;
eeprom.usec_delay_step--;
}
eeprom.adapt_state = -1;
}
if (eeprom.adapt_state > -100)
{
eeprom.adapt_state--;
}
else
{
/* Restart adaption */
D(printk("#Restart\n"));
eeprom.usec_delay_step++;
}
}
#endif /* EEPROM_ADAPTIVE_TIMING */
/* write until we hit a page boundary or count */
do
{
i2c_outbyte(buf[written]);
if(!i2c_getack())
{
restart=1;
printk(KERN_INFO "%s: write error, retrying. %d\n", eeprom_name, i);
i2c_stop();
break;
}
written++;
p++;
} while( written < count && ( p % eeprom.sequential_write_pagesize ));
/* end write cycle */
i2c_stop();
i2c_delay(eeprom.usec_delay_writecycles);
} /* while */
} /* for */
mutex_unlock(&eeprom.lock);
if (written == 0 && p >= eeprom.size){
return -ENOSPC;
}
*off = p;
return written;
}
/* Closes the device. */
static int eeprom_close(struct inode * inode, struct file * file)
{
/* do nothing for now */
return 0;
}
/* Sets the current address of the eeprom. */
static int eeprom_address(unsigned long addr)
{
int i;
unsigned char page, offset;
page = (unsigned char) (addr >> 8);
offset = (unsigned char) addr;
for(i = 0; i < EEPROM_RETRIES; i++)
{
/* start a dummy write for addressing */
i2c_start();
if(eeprom.size == EEPROM_16KB)
{
i2c_outbyte( eeprom.select_cmd );
i2c_getack();
i2c_outbyte(page);
}
else
{
i2c_outbyte( eeprom.select_cmd | (page << 1) );
}
if(!i2c_getack())
{
/* retry */
i2c_stop();
/* Must have a delay here.. 500 works, >50, 100->works 5th time*/
i2c_delay(MAX_WRITEDELAY_US / EEPROM_RETRIES * i);
/* The chip needs up to 10 ms from write stop to next start */
}
else
{
i2c_outbyte(offset);
if(!i2c_getack())
{
/* retry */
i2c_stop();
}
else
break;
}
}
eeprom.retry_cnt_addr = i;
D(printk("%i\n", eeprom.retry_cnt_addr));
if(eeprom.retry_cnt_addr == EEPROM_RETRIES)
{
/* failed */
return 0;
}
return 1;
}
/* Reads from current address. */
static int read_from_eeprom(char * buf, int count)
{
int i, read=0;
for(i = 0; i < EEPROM_RETRIES; i++)
{
if(eeprom.size == EEPROM_16KB)
{
i2c_outbyte( eeprom.select_cmd | 1 );
}
if(i2c_getack())
{
break;
}
}
if(i == EEPROM_RETRIES)
{
printk(KERN_INFO "%s: failed to read from eeprom\n", eeprom_name);
i2c_stop();
return -EFAULT;
}
while( (read < count))
{
if (put_user(i2c_inbyte(), &buf[read++]))
{
i2c_stop();
return -EFAULT;
}
/*
* make sure we don't ack last byte or you will get very strange
* results!
*/
if(read < count)
{
i2c_sendack();
}
}
/* stop the operation */
i2c_stop();
return read;
}
/* Disables write protection if applicable. */
#define DBP_SAVE(x)
#define ax_printf printk
static void eeprom_disable_write_protect(void)
{
/* Disable write protect */
if (eeprom.size == EEPROM_8KB)
{
/* Step 1 Set WEL = 1 (write 00000010 to address 1FFFh */
i2c_start();
i2c_outbyte(0xbe);
if(!i2c_getack())
{
DBP_SAVE(ax_printf("Get ack returns false\n"));
}
i2c_outbyte(0xFF);
if(!i2c_getack())
{
DBP_SAVE(ax_printf("Get ack returns false 2\n"));
}
i2c_outbyte(0x02);
if(!i2c_getack())
{
DBP_SAVE(ax_printf("Get ack returns false 3\n"));
}
i2c_stop();
i2c_delay(1000);
/* Step 2 Set RWEL = 1 (write 00000110 to address 1FFFh */
i2c_start();
i2c_outbyte(0xbe);
if(!i2c_getack())
{
DBP_SAVE(ax_printf("Get ack returns false 55\n"));
}
i2c_outbyte(0xFF);
if(!i2c_getack())
{
DBP_SAVE(ax_printf("Get ack returns false 52\n"));
}
i2c_outbyte(0x06);
if(!i2c_getack())
{
DBP_SAVE(ax_printf("Get ack returns false 53\n"));
}
i2c_stop();
/* Step 3 Set BP1, BP0, and/or WPEN bits (write 00000110 to address 1FFFh */
i2c_start();
i2c_outbyte(0xbe);
if(!i2c_getack())
{
DBP_SAVE(ax_printf("Get ack returns false 56\n"));
}
i2c_outbyte(0xFF);
if(!i2c_getack())
{
DBP_SAVE(ax_printf("Get ack returns false 57\n"));
}
i2c_outbyte(0x06);
if(!i2c_getack())
{
DBP_SAVE(ax_printf("Get ack returns false 58\n"));
}
i2c_stop();
/* Write protect disabled */
}
}
device_initcall(eeprom_init);

View File

@ -1,857 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Etrax general port I/O device
*
* Copyright (c) 1999-2007 Axis Communications AB
*
* Authors: Bjorn Wesen (initial version)
* Ola Knutsson (LED handling)
* Johan Adolfsson (read/set directions, write, port G)
*/
#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 <asm/etraxgpio.h>
#include <arch/svinto.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <arch/io_interface_mux.h>
#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 long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static ssize_t gpio_write(struct file *file, const char __user *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 __poll_t gpio_poll(struct file *filp, struct poll_table_struct *wait);
/* private data per open() of this driver */
struct gpio_private {
struct gpio_private *next;
/* These fields are for PA and PB only */
volatile unsigned char *port, *shadow;
volatile unsigned char *dir, *dir_shadow;
unsigned char changeable_dir;
unsigned char changeable_bits;
unsigned char clk_mask;
unsigned char data_mask;
unsigned char write_msb;
unsigned char pad1, pad2, pad3;
/* 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;
static int gpio_some_alarms; /* Set if someone uses alarm */
static unsigned long gpio_pa_irq_enabled_mask;
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)
static volatile unsigned char *ports[NUM_PORTS] = {
R_PORT_PA_DATA,
R_PORT_PB_DATA,
};
static volatile unsigned char *shads[NUM_PORTS] = {
&port_pa_data_shadow,
&port_pb_data_shadow
};
/* What direction bits that are user changeable 1=changeable*/
#ifndef CONFIG_ETRAX_PA_CHANGEABLE_DIR
#define CONFIG_ETRAX_PA_CHANGEABLE_DIR 0x00
#endif
#ifndef CONFIG_ETRAX_PB_CHANGEABLE_DIR
#define CONFIG_ETRAX_PB_CHANGEABLE_DIR 0x00
#endif
#ifndef CONFIG_ETRAX_PA_CHANGEABLE_BITS
#define CONFIG_ETRAX_PA_CHANGEABLE_BITS 0xFF
#endif
#ifndef CONFIG_ETRAX_PB_CHANGEABLE_BITS
#define CONFIG_ETRAX_PB_CHANGEABLE_BITS 0xFF
#endif
static unsigned char changeable_dir[NUM_PORTS] = {
CONFIG_ETRAX_PA_CHANGEABLE_DIR,
CONFIG_ETRAX_PB_CHANGEABLE_DIR
};
static unsigned char changeable_bits[NUM_PORTS] = {
CONFIG_ETRAX_PA_CHANGEABLE_BITS,
CONFIG_ETRAX_PB_CHANGEABLE_BITS
};
static volatile unsigned char *dir[NUM_PORTS] = {
R_PORT_PA_DIR,
R_PORT_PB_DIR
};
static volatile unsigned char *dir_shadow[NUM_PORTS] = {
&port_pa_dir_shadow,
&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. */
static unsigned long changeable_dir_g;
static unsigned long dir_g_in_bits;
static unsigned long dir_g_out_bits;
static unsigned long dir_g_shadow; /* 1=output */
#define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B)
static __poll_t gpio_poll(struct file *file, poll_table *wait)
{
__poll_t mask = 0;
struct gpio_private *priv = file->private_data;
unsigned long data;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
poll_wait(file, &priv->alarm_wq, wait);
if (priv->minor == GPIO_MINOR_A) {
unsigned long tmp;
data = *R_PORT_PA_DATA;
/* PA has support for high level interrupt -
* lets activate for those low and with highalarm set
*/
tmp = ~data & priv->highalarm & 0xFF;
tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
gpio_pa_irq_enabled_mask |= tmp;
*R_IRQ_MASK1_SET = tmp;
} else if (priv->minor == GPIO_MINOR_B)
data = *R_PORT_PB_DATA;
else if (priv->minor == GPIO_MINOR_G)
data = *R_PORT_G_DATA;
else {
mask = 0;
goto out;
}
if ((data & priv->highalarm) ||
(~data & priv->lowalarm)) {
mask = EPOLLIN|EPOLLRDNORM;
}
out:
spin_unlock_irqrestore(&gpio_lock, flags);
DP(printk("gpio_poll ready: mask 0x%08X\n", mask));
return mask;
}
int etrax_gpio_wake_up_check(void)
{
struct gpio_private *priv;
unsigned long data = 0;
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
priv = alarmlist;
while (priv) {
if (USE_PORTS(priv))
data = *priv->port;
else if (priv->minor == GPIO_MINOR_G)
data = *R_PORT_G_DATA;
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;
}
spin_unlock_irqrestore(&gpio_lock, flags);
return ret;
}
static irqreturn_t
gpio_poll_timer_interrupt(int irq, void *dev_id)
{
if (gpio_some_alarms) {
etrax_gpio_wake_up_check();
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static irqreturn_t
gpio_interrupt(int irq, void *dev_id)
{
unsigned long tmp;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
/* Find what PA interrupts are active */
tmp = (*R_IRQ_READ1);
/* Find those that we have enabled */
tmp &= gpio_pa_irq_enabled_mask;
/* Clear them.. */
*R_IRQ_MASK1_CLR = tmp;
gpio_pa_irq_enabled_mask &= ~tmp;
spin_unlock_irqrestore(&gpio_lock, flags);
if (gpio_some_alarms)
return IRQ_RETVAL(etrax_gpio_wake_up_check());
return IRQ_NONE;
}
static void gpio_write_bit(struct gpio_private *priv,
unsigned char data, int bit)
{
*priv->port = *priv->shadow &= ~(priv->clk_mask);
if (data & 1 << bit)
*priv->port = *priv->shadow |= priv->data_mask;
else
*priv->port = *priv->shadow &= ~(priv->data_mask);
/* For FPGA: min 5.0ns (DCC) before CCLK high */
*priv->port = *priv->shadow |= priv->clk_mask;
}
static void gpio_write_byte(struct gpio_private *priv, unsigned char data)
{
int i;
if (priv->write_msb)
for (i = 7; i >= 0; i--)
gpio_write_bit(priv, data, i);
else
for (i = 0; i <= 7; i++)
gpio_write_bit(priv, data, i);
}
static ssize_t gpio_write(struct file *file, const char __user *buf,
size_t count, loff_t *off)
{
struct gpio_private *priv = file->private_data;
unsigned long flags;
ssize_t retval = count;
if (priv->minor != GPIO_MINOR_A && priv->minor != GPIO_MINOR_B)
return -EFAULT;
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
spin_lock_irqsave(&gpio_lock, flags);
/* It must have been configured using the IO_CFG_WRITE_MODE */
/* Perhaps a better error code? */
if (priv->clk_mask == 0 || priv->data_mask == 0) {
retval = -EPERM;
goto out;
}
D(printk(KERN_DEBUG "gpio_write: %02X to data 0x%02X "
"clk 0x%02X msb: %i\n",
count, priv->data_mask, priv->clk_mask, priv->write_msb));
while (count--)
gpio_write_byte(priv, *buf++);
out:
spin_unlock_irqrestore(&gpio_lock, flags);
return retval;
}
static int
gpio_open(struct inode *inode, struct file *filp)
{
struct gpio_private *priv;
int p = iminor(inode);
unsigned long flags;
if (p > GPIO_MINOR_LAST)
return -EINVAL;
priv = kzalloc(sizeof(struct gpio_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->minor = p;
/* initialize the io/alarm struct */
if (USE_PORTS(priv)) { /* A and B */
priv->port = ports[p];
priv->shadow = shads[p];
priv->dir = dir[p];
priv->dir_shadow = dir_shadow[p];
priv->changeable_dir = changeable_dir[p];
priv->changeable_bits = changeable_bits[p];
} else {
priv->port = NULL;
priv->shadow = NULL;
priv->dir = NULL;
priv->dir_shadow = NULL;
priv->changeable_dir = 0;
priv->changeable_bits = 0;
}
priv->highalarm = 0;
priv->lowalarm = 0;
priv->clk_mask = 0;
priv->data_mask = 0;
init_waitqueue_head(&priv->alarm_wq);
filp->private_data = priv;
/* link it into our alarmlist */
spin_lock_irqsave(&gpio_lock, flags);
priv->next = alarmlist;
alarmlist = priv;
spin_unlock_irqrestore(&gpio_lock, flags);
return 0;
}
static int
gpio_release(struct inode *inode, struct file *filp)
{
struct gpio_private *p;
struct gpio_private *todel;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
p = alarmlist;
todel = filp->private_data;
/* 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;
while (p) {
if (p->highalarm | p->lowalarm) {
gpio_some_alarms = 1;
goto out;
}
p = p->next;
}
gpio_some_alarms = 0;
out:
spin_unlock_irqrestore(&gpio_lock, flags);
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().
*/
inline unsigned long setget_input(struct gpio_private *priv, unsigned long arg)
{
/* Set direction 0=unchanged 1=input,
* return mask with 1=input */
if (USE_PORTS(priv)) {
*priv->dir = *priv->dir_shadow &=
~((unsigned char)arg & priv->changeable_dir);
return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */
}
if (priv->minor != GPIO_MINOR_G)
return 0;
/* We must fiddle with R_GEN_CONFIG to change dir */
if (((arg & dir_g_in_bits) != arg) &&
(arg & changeable_dir_g)) {
arg &= changeable_dir_g;
/* Clear bits in genconfig to set to input */
if (arg & (1<<0)) {
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g0dir);
dir_g_in_bits |= (1<<0);
dir_g_out_bits &= ~(1<<0);
}
if ((arg & 0x0000FF00) == 0x0000FF00) {
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g8_15dir);
dir_g_in_bits |= 0x0000FF00;
dir_g_out_bits &= ~0x0000FF00;
}
if ((arg & 0x00FF0000) == 0x00FF0000) {
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g16_23dir);
dir_g_in_bits |= 0x00FF0000;
dir_g_out_bits &= ~0x00FF0000;
}
if (arg & (1<<24)) {
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g24dir);
dir_g_in_bits |= (1<<24);
dir_g_out_bits &= ~(1<<24);
}
D(printk(KERN_DEBUG "gpio: SETINPUT on port G set "
"genconfig to 0x%08lX "
"in_bits: 0x%08lX "
"out_bits: 0x%08lX\n",
(unsigned long)genconfig_shadow,
dir_g_in_bits, dir_g_out_bits));
*R_GEN_CONFIG = genconfig_shadow;
/* Must be a >120 ns delay before writing this again */
}
return dir_g_in_bits;
} /* setget_input */
inline unsigned long setget_output(struct gpio_private *priv, unsigned long arg)
{
if (USE_PORTS(priv)) {
*priv->dir = *priv->dir_shadow |=
((unsigned char)arg & priv->changeable_dir);
return *priv->dir_shadow;
}
if (priv->minor != GPIO_MINOR_G)
return 0;
/* We must fiddle with R_GEN_CONFIG to change dir */
if (((arg & dir_g_out_bits) != arg) &&
(arg & changeable_dir_g)) {
/* Set bits in genconfig to set to output */
if (arg & (1<<0)) {
genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g0dir);
dir_g_out_bits |= (1<<0);
dir_g_in_bits &= ~(1<<0);
}
if ((arg & 0x0000FF00) == 0x0000FF00) {
genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g8_15dir);
dir_g_out_bits |= 0x0000FF00;
dir_g_in_bits &= ~0x0000FF00;
}
if ((arg & 0x00FF0000) == 0x00FF0000) {
genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g16_23dir);
dir_g_out_bits |= 0x00FF0000;
dir_g_in_bits &= ~0x00FF0000;
}
if (arg & (1<<24)) {
genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g24dir);
dir_g_out_bits |= (1<<24);
dir_g_in_bits &= ~(1<<24);
}
D(printk(KERN_INFO "gpio: SETOUTPUT on port G set "
"genconfig to 0x%08lX "
"in_bits: 0x%08lX "
"out_bits: 0x%08lX\n",
(unsigned long)genconfig_shadow,
dir_g_in_bits, dir_g_out_bits));
*R_GEN_CONFIG = genconfig_shadow;
/* Must be a >120 ns delay before writing this again */
}
return dir_g_out_bits & 0x7FFFFFFF;
} /* setget_output */
static int
gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned long flags;
unsigned long val;
int ret = 0;
struct gpio_private *priv = 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
spin_lock_irqsave(&gpio_lock, flags);
if (USE_PORTS(priv)) {
ret = *priv->port;
} else if (priv->minor == GPIO_MINOR_G) {
ret = (*R_PORT_G_DATA) & 0x7FFFFFFF;
}
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_SETBITS:
// set changeable bits with a 1 in arg
spin_lock_irqsave(&gpio_lock, flags);
if (USE_PORTS(priv)) {
*priv->port = *priv->shadow |=
((unsigned char)arg & priv->changeable_bits);
} else if (priv->minor == GPIO_MINOR_G) {
*R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits);
}
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_CLRBITS:
// clear changeable bits with a 1 in arg
spin_lock_irqsave(&gpio_lock, flags);
if (USE_PORTS(priv)) {
*priv->port = *priv->shadow &=
~((unsigned char)arg & priv->changeable_bits);
} else if (priv->minor == GPIO_MINOR_G) {
*R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits);
}
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_HIGHALARM:
// set alarm when bits with 1 in arg go high
spin_lock_irqsave(&gpio_lock, flags);
priv->highalarm |= arg;
gpio_some_alarms = 1;
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_LOWALARM:
// set alarm when bits with 1 in arg go low
spin_lock_irqsave(&gpio_lock, flags);
priv->lowalarm |= arg;
gpio_some_alarms = 1;
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_CLRALARM:
/* clear alarm for bits with 1 in arg */
spin_lock_irqsave(&gpio_lock, flags);
priv->highalarm &= ~arg;
priv->lowalarm &= ~arg;
{
/* Must update gpio_some_alarms */
struct gpio_private *p = alarmlist;
int some_alarms;
p = alarmlist;
some_alarms = 0;
while (p) {
if (p->highalarm | p->lowalarm) {
some_alarms = 1;
break;
}
p = p->next;
}
gpio_some_alarms = some_alarms;
}
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
/* Read direction 0=input 1=output */
spin_lock_irqsave(&gpio_lock, flags);
if (USE_PORTS(priv)) {
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.
*/
ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF;
}
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
/* Set direction 0=unchanged 1=input,
* return mask with 1=input
*/
spin_lock_irqsave(&gpio_lock, flags);
ret = setget_input(priv, arg) & 0x7FFFFFFF;
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
/* Set direction 0=unchanged 1=output,
* return mask with 1=output
*/
spin_lock_irqsave(&gpio_lock, flags);
ret = setget_output(priv, arg) & 0x7FFFFFFF;
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_SHUTDOWN:
spin_lock_irqsave(&gpio_lock, flags);
SOFT_SHUTDOWN();
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_GET_PWR_BT:
spin_lock_irqsave(&gpio_lock, flags);
#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN)
ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));
#else
ret = 0;
#endif
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_CFG_WRITE_MODE:
spin_lock_irqsave(&gpio_lock, flags);
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 & priv->changeable_bits) &&
(priv->data_mask & priv->changeable_bits) &&
(priv->clk_mask & *priv->dir_shadow) &&
(priv->data_mask & *priv->dir_shadow)))
{
priv->clk_mask = 0;
priv->data_mask = 0;
ret = -EPERM;
}
spin_unlock_irqrestore(&gpio_lock, flags);
break;
case IO_READ_INBITS:
/* *arg is result of reading the input pins */
spin_lock_irqsave(&gpio_lock, flags);
if (USE_PORTS(priv)) {
val = *priv->port;
} else if (priv->minor == GPIO_MINOR_G) {
val = *R_PORT_G_DATA;
}
spin_unlock_irqrestore(&gpio_lock, flags);
if (copy_to_user((void __user *)arg, &val, sizeof(val)))
ret = -EFAULT;
break;
case IO_READ_OUTBITS:
/* *arg is result of reading the output shadow */
spin_lock_irqsave(&gpio_lock, flags);
if (USE_PORTS(priv)) {
val = *priv->shadow;
} else if (priv->minor == GPIO_MINOR_G) {
val = port_g_data_shadow;
}
spin_unlock_irqrestore(&gpio_lock, flags);
if (copy_to_user((void __user *)arg, &val, sizeof(val)))
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, (void __user *)arg, sizeof(val)))
{
ret = -EFAULT;
break;
}
spin_lock_irqsave(&gpio_lock, flags);
val = setget_input(priv, val);
spin_unlock_irqrestore(&gpio_lock, flags);
if (copy_to_user((void __user *)arg, &val, sizeof(val)))
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, (void __user *)arg, sizeof(val))) {
ret = -EFAULT;
break;
}
spin_lock_irqsave(&gpio_lock, flags);
val = setget_output(priv, val);
spin_unlock_irqrestore(&gpio_lock, flags);
if (copy_to_user((void __user *)arg, &val, sizeof(val)))
ret = -EFAULT;
break;
default:
spin_lock_irqsave(&gpio_lock, flags);
if (priv->minor == GPIO_MINOR_LEDS)
ret = gpio_leds_ioctl(cmd, arg);
else
ret = -EINVAL;
spin_unlock_irqrestore(&gpio_lock, flags);
} /* switch */
return ret;
}
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;
CRIS_LED_ACTIVE_SET_G(green);
CRIS_LED_ACTIVE_SET_R(red);
break;
case IO_LED_SETBIT:
CRIS_LED_BIT_SET(arg);
break;
case IO_LED_CLRBIT:
CRIS_LED_BIT_CLR(arg);
break;
default:
return -EINVAL;
} /* switch */
return 0;
}
static const struct file_operations gpio_fops = {
.owner = THIS_MODULE,
.poll = gpio_poll,
.unlocked_ioctl = gpio_ioctl,
.write = gpio_write,
.open = gpio_open,
.release = gpio_release,
.llseek = noop_llseek,
};
static 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)
{
unsigned long int flags;
D(printk(KERN_DEBUG "gpio.c: ioif_watcher called\n"));
D(printk(KERN_DEBUG "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));
spin_lock_irqsave(&gpio_lock, flags);
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 */
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out))
dir_g_shadow |= (1 << 0);
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out))
dir_g_shadow |= 0x0000FF00;
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g16_23dir, out))
dir_g_shadow |= 0x00FF0000;
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out))
dir_g_shadow |= (1 << 24);
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 */
dir_g_out_bits &= ~changeable_dir_g;
dir_g_out_bits |= dir_g_shadow;
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);
printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n",
dir_g_shadow, changeable_dir_g);
}
/* main driver initialization routine, called from mem.c */
static int __init gpio_init(void)
{
int res;
#if defined (CONFIG_ETRAX_CSP0_LEDS)
int i;
#endif
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 */
#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS)
CRIS_LED_NETWORK_SET(0);
CRIS_LED_ACTIVE_SET(0);
CRIS_LED_DISK_READ(0);
CRIS_LED_DISK_WRITE(0);
#if defined (CONFIG_ETRAX_CSP0_LEDS)
for (i = 0; i < 32; i++)
CRIS_LED_BIT_SET(i);
#endif
#endif
/* 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-2008 "
"Axis Communications AB\n");
/* We call etrax_gpio_wake_up_check() from timer interrupt and
* from default_idle() in kernel/process.c
* The check in default_idle() reduces latency from ~15 ms to ~6 ms
* in some tests.
*/
res = request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt,
IRQF_SHARED, "gpio poll", gpio_name);
if (res) {
printk(KERN_CRIT "err: timer0 irq for gpio\n");
return res;
}
res = request_irq(PA_IRQ_NBR, gpio_interrupt,
IRQF_SHARED, "gpio PA", gpio_name);
if (res)
printk(KERN_CRIT "err: PA irq for gpio\n");
return res;
}
/* this makes sure that gpio_init is called during kernel boot */
module_init(gpio_init);

View File

@ -1,699 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*!***************************************************************************
*!
*! 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
*!
*! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN
*!
*!***************************************************************************/
/****************** INCLUDE FILES SECTION ***********************************/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
#include <asm/etraxi2c.h>
#include <arch/svinto.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <arch/io_interface_mux.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
#ifdef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C
/* Use PB and not PB_I2C */
#ifndef CONFIG_ETRAX_I2C_DATA_PORT
#define CONFIG_ETRAX_I2C_DATA_PORT 0
#endif
#ifndef CONFIG_ETRAX_I2C_CLK_PORT
#define CONFIG_ETRAX_I2C_CLK_PORT 1
#endif
#define SDABIT CONFIG_ETRAX_I2C_DATA_PORT
#define SCLBIT CONFIG_ETRAX_I2C_CLK_PORT
#define i2c_enable()
#define i2c_disable()
/* enable or disable output-enable, to select output or input on the i2c bus */
#define i2c_dir_out() \
REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, SDABIT, 1)
#define i2c_dir_in() \
REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, SDABIT, 0)
/* control the i2c clock and data signals */
#define i2c_clk(x) \
REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, SCLBIT, x)
#define i2c_data(x) \
REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, SDABIT, x)
/* read a bit from the i2c interface */
#define i2c_getbit() (((*R_PORT_PB_READ & (1 << SDABIT))) >> SDABIT)
#else
/* enable or disable the i2c interface */
#define i2c_enable() *R_PORT_PB_I2C = (port_pb_i2c_shadow |= IO_MASK(R_PORT_PB_I2C, i2c_en))
#define i2c_disable() *R_PORT_PB_I2C = (port_pb_i2c_shadow &= ~IO_MASK(R_PORT_PB_I2C, i2c_en))
/* enable or disable output-enable, to select output or input on the i2c bus */
#define i2c_dir_out() \
*R_PORT_PB_I2C = (port_pb_i2c_shadow &= ~IO_MASK(R_PORT_PB_I2C, i2c_oe_)); \
REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, 0, 1);
#define i2c_dir_in() \
*R_PORT_PB_I2C = (port_pb_i2c_shadow |= IO_MASK(R_PORT_PB_I2C, i2c_oe_)); \
REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, 0, 0);
/* control the i2c clock and data signals */
#define i2c_clk(x) \
*R_PORT_PB_I2C = (port_pb_i2c_shadow = (port_pb_i2c_shadow & \
~IO_MASK(R_PORT_PB_I2C, i2c_clk)) | IO_FIELD(R_PORT_PB_I2C, i2c_clk, (x))); \
REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 1, x);
#define i2c_data(x) \
*R_PORT_PB_I2C = (port_pb_i2c_shadow = (port_pb_i2c_shadow & \
~IO_MASK(R_PORT_PB_I2C, i2c_d)) | IO_FIELD(R_PORT_PB_I2C, i2c_d, (x))); \
REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 0, x);
/* read a bit from the i2c interface */
#define i2c_getbit() (*R_PORT_PB_READ & 0x1)
#endif
/* use the kernels delay routine */
#define i2c_delay(usecs) udelay(usecs)
static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */
/****************** 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;
spin_lock(&i2c_lock);
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);
spin_unlock(&i2c_lock);
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;
spin_lock(&i2c_lock);
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--);
spin_unlock(&i2c_lock);
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 long i2c_ioctl(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(KERN_DEBUG "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(KERN_DEBUG "i2cr %d %d ",
I2C_ARGSLAVE(arg),
I2C_ARGREG(arg)));
val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg));
D(printk(KERN_DEBUG "= %d\n", val));
return val;
}
default:
return -EINVAL;
}
return 0;
}
static const struct file_operations i2c_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = i2c_ioctl,
.open = i2c_open,
.release = i2c_release,
.llseek = noop_llseek,
};
int __init
i2c_init(void)
{
static int res = 0;
static int first = 1;
if (!first) {
return res;
}
first = 0;
/* 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);
port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir0);
port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir1);
*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 res;
}
static int __init
i2c_register(void)
{
int res;
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-2004 Axis Communications AB\n");
return 0;
}
/* this makes sure that i2c_register is called during boot */
module_init(i2c_register);
/****************** END OF FILE i2c.c ********************************/

View File

@ -1,18 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* i2c.h */
int i2c_init(void);
/* High level I2C actions */
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);

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the linux kernel.
#
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 \
dma.o io_interface_mux.o
obj-$(CONFIG_ETRAX_KGDB) += kgdb.o
obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o
obj-$(CONFIG_MODULES) += crisksyms.o
clean:

View File

@ -1,17 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <asm/io.h>
#include <arch/svinto.h>
/* Export shadow registers for the CPU I/O pins */
EXPORT_SYMBOL(genconfig_shadow);
EXPORT_SYMBOL(port_pa_data_shadow);
EXPORT_SYMBOL(port_pa_dir_shadow);
EXPORT_SYMBOL(port_pb_data_shadow);
EXPORT_SYMBOL(port_pb_dir_shadow);
EXPORT_SYMBOL(port_pb_config_shadow);
EXPORT_SYMBOL(port_g_data_shadow);
/* Cache flush functions */
EXPORT_SYMBOL(flush_etrax_cache);
EXPORT_SYMBOL(prepare_rx_descriptor);

View File

@ -1,560 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/* Serialport functions for debugging
*
* Copyright (c) 2000-2007 Axis Communications AB
*
* Authors: Bjorn Wesen
*
* Exports:
* console_print_etrax(char *buf)
* int getDebugChar()
* putDebugChar(int)
* enableDebugIRQ()
* init_etrax_debug()
*
*/
#include <linux/console.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/delay.h>
#include <linux/tty.h>
#include <arch/svinto.h>
extern void reset_watchdog(void);
struct dbg_port
{
unsigned int index;
const volatile unsigned* read;
volatile char* write;
volatile unsigned* xoff;
volatile char* baud;
volatile char* tr_ctrl;
volatile char* rec_ctrl;
unsigned long irq;
unsigned int started;
unsigned long baudrate;
unsigned char parity;
unsigned int bits;
};
struct dbg_port ports[]=
{
{
0,
R_SERIAL0_READ,
R_SERIAL0_TR_DATA,
R_SERIAL0_XOFF,
R_SERIAL0_BAUD,
R_SERIAL0_TR_CTRL,
R_SERIAL0_REC_CTRL,
IO_STATE(R_IRQ_MASK1_SET, ser0_data, set),
0,
115200,
'N',
8
},
{
1,
R_SERIAL1_READ,
R_SERIAL1_TR_DATA,
R_SERIAL1_XOFF,
R_SERIAL1_BAUD,
R_SERIAL1_TR_CTRL,
R_SERIAL1_REC_CTRL,
IO_STATE(R_IRQ_MASK1_SET, ser1_data, set),
0,
115200,
'N',
8
},
{
2,
R_SERIAL2_READ,
R_SERIAL2_TR_DATA,
R_SERIAL2_XOFF,
R_SERIAL2_BAUD,
R_SERIAL2_TR_CTRL,
R_SERIAL2_REC_CTRL,
IO_STATE(R_IRQ_MASK1_SET, ser2_data, set),
0,
115200,
'N',
8
},
{
3,
R_SERIAL3_READ,
R_SERIAL3_TR_DATA,
R_SERIAL3_XOFF,
R_SERIAL3_BAUD,
R_SERIAL3_TR_CTRL,
R_SERIAL3_REC_CTRL,
IO_STATE(R_IRQ_MASK1_SET, ser3_data, set),
0,
115200,
'N',
8
}
};
#ifdef CONFIG_ETRAX_SERIAL
extern struct tty_driver *serial_driver;
#endif
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
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(struct dbg_port* p)
{
unsigned long rec_ctrl = 0;
unsigned long tr_ctrl = 0;
if (!p)
return;
if (p->started)
return;
p->started = 1;
if (p->index == 0)
{
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6);
genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
}
else if (p->index == 1)
{
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8);
genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
}
else if (p->index == 2)
{
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2);
genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0);
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma3);
genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, par0);
genconfig_shadow |= IO_STATE(R_GEN_CONFIG, ser2, select);
}
else
{
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma4);
genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1);
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma5);
genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, par1);
genconfig_shadow |= IO_STATE(R_GEN_CONFIG, ser3, select);
}
*R_GEN_CONFIG = genconfig_shadow;
*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 (p->baudrate)
{
case 0:
case 115200:
*p->baud =
IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) |
IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz);
break;
case 1200:
*p->baud =
IO_STATE(R_SERIAL0_BAUD, tr_baud, c1200Hz) |
IO_STATE(R_SERIAL0_BAUD, rec_baud, c1200Hz);
break;
case 2400:
*p->baud =
IO_STATE(R_SERIAL0_BAUD, tr_baud, c2400Hz) |
IO_STATE(R_SERIAL0_BAUD, rec_baud, c2400Hz);
break;
case 4800:
*p->baud =
IO_STATE(R_SERIAL0_BAUD, tr_baud, c4800Hz) |
IO_STATE(R_SERIAL0_BAUD, rec_baud, c4800Hz);
break;
case 9600:
*p->baud =
IO_STATE(R_SERIAL0_BAUD, tr_baud, c9600Hz) |
IO_STATE(R_SERIAL0_BAUD, rec_baud, c9600Hz);
break;
case 19200:
*p->baud =
IO_STATE(R_SERIAL0_BAUD, tr_baud, c19k2Hz) |
IO_STATE(R_SERIAL0_BAUD, rec_baud, c19k2Hz);
break;
case 38400:
*p->baud =
IO_STATE(R_SERIAL0_BAUD, tr_baud, c38k4Hz) |
IO_STATE(R_SERIAL0_BAUD, rec_baud, c38k4Hz);
break;
case 57600:
*p->baud =
IO_STATE(R_SERIAL0_BAUD, tr_baud, c57k6Hz) |
IO_STATE(R_SERIAL0_BAUD, rec_baud, c57k6Hz);
break;
default:
*p->baud =
IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) |
IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz);
break;
}
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 (p->parity == 'O') {
rec_ctrl =
IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd) |
IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
tr_ctrl =
IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd) |
IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, enable);
} else {
rec_ctrl =
IO_STATE(R_SERIAL0_REC_CTRL, rec_par, even) |
IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, disable);
tr_ctrl =
IO_STATE(R_SERIAL0_TR_CTRL, tr_par, even) |
IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, disable);
}
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);
}
else
{
rec_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_bitnr, rec_8bit);
tr_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_8bit);
}
*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) |
IO_STATE(R_SERIAL0_REC_CTRL, sampling, middle) |
IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, normal) |
rec_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) |
IO_STATE(R_SERIAL0_TR_CTRL, stop_bits, one_bit) |
IO_STATE(R_SERIAL0_TR_CTRL, tr_stick_par, normal) |
tr_ctrl;
}
static void
console_write_direct(struct console *co, const char *buf, unsigned int len)
{
int i;
unsigned long flags;
if (!port)
return;
local_irq_save(flags);
/* 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)))
;
*port->write = buf[i];
}
/*
* Feed the watchdog, otherwise it will reset the chip during boot.
* The time to send an ordinary boot message line (10-90 chars)
* varies between 1-8ms at 115200. What makes up for the additional
* 90ms that allows the watchdog to bite?
*/
reset_watchdog();
local_irq_restore(flags);
}
static void
console_write(struct console *co, const char *buf, unsigned int len)
{
if (!port)
return;
console_write_direct(co, buf, len);
}
/* legacy function */
void
console_print_etrax(const char *buf)
{
console_write(NULL, buf, strlen(buf));
}
/* Use polling to get a single character FROM the debug port */
int
getDebugChar(void)
{
unsigned long readval;
if (!kgdb_port)
return 0;
do {
readval = *kgdb_port->read;
} while (!(readval & IO_MASK(R_SERIAL0_READ, data_avail)));
return (readval & IO_MASK(R_SERIAL0_READ, data_in));
}
/* Use polling to put a single character to the debug port */
void
putDebugChar(int val)
{
if (!kgdb_port)
return;
while (!(*kgdb_port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
;
*kgdb_port->write = val;
}
/* Enable irq for receiving chars on the debug port, used by kgdb */
void
enableDebugIRQ(void)
{
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);
*kgdb_port->rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable);
}
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(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;
}
static const struct tty_operations dummy_ops = {
.open = dummy_open,
.close = dummy_close,
.write = dummy_write,
.write_room = dummy_write_room,
};
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;
/* Normally B9600 default... */
dummy_driver.init_termios.c_cflag =
B115200 | CS8 | CREAD | HUPCL | CLOCAL;
dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
dummy_driver.init_termios.c_ispeed = 115200;
dummy_driver.init_termios.c_ospeed = 115200;
dummy_driver.ops = &dummy_ops;
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;
else
*index = 0;
#ifdef CONFIG_ETRAX_SERIAL
return port ? serial_driver : &dummy_driver;
#else
return &dummy_driver;
#endif
}
static struct console ser_console = {
name : "ttyS",
write: console_write,
read : NULL,
device : etrax_console_device,
unblank : NULL,
setup : console_setup,
flags : CON_PRINTBUFFER,
index : -1,
cflag : 0,
next : NULL
};
static struct console ser0_console = {
name : "ttyS",
write: console_write,
read : NULL,
device : etrax_console_device,
unblank : NULL,
setup : console_setup,
flags : CON_PRINTBUFFER,
index : 0,
cflag : 0,
next : NULL
};
static struct console ser1_console = {
name : "ttyS",
write: console_write,
read : NULL,
device : etrax_console_device,
unblank : NULL,
setup : console_setup,
flags : CON_PRINTBUFFER,
index : 1,
cflag : 0,
next : NULL
};
static struct console ser2_console = {
name : "ttyS",
write: console_write,
read : NULL,
device : etrax_console_device,
unblank : NULL,
setup : console_setup,
flags : CON_PRINTBUFFER,
index : 2,
cflag : 0,
next : NULL
};
static struct console ser3_console = {
name : "ttyS",
write: console_write,
read : NULL,
device : etrax_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(&ser_console);
register_console(&ser0_console);
register_console(&ser1_console);
register_console(&ser2_console);
register_console(&ser3_console);
init_dummy_console();
return 0;
}
first = 0;
register_console(&ser_console);
start_port(port);
#ifdef CONFIG_ETRAX_KGDB
start_port(kgdb_port);
#endif
return 0;
}
__initcall(init_etrax_debug);

View File

@ -1,288 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/* Wrapper for DMA channel allocator that updates DMA client muxing.
* Copyright 2004-2007, Axis Communications AB
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <asm/dma.h>
#include <arch/svinto.h>
#include <arch/system.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 >= 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 >= 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,978 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* linux/arch/cris/entry.S
*
* Copyright (C) 2000, 2001, 2002 Axis Communications AB
*
* Authors: Bjorn Wesen (bjornw@axis.com)
*/
/*
* entry.S contains the system-call and fault low-level handling routines.
*
* NOTE: This code handles signal-recognition, which happens every time
* after a timer-interrupt and after each system call.
*
* Stack layout in 'ret_from_system_call':
* ptrace needs to have all regs on the stack.
* if the order here is changed, it needs to be
* updated in fork.c:copy_process, signal.c:do_signal,
* ptrace.c and ptrace.h
*
*/
#include <linux/linkage.h>
#include <linux/sys.h>
#include <asm/unistd.h>
#include <arch/sv_addr_ag.h>
#include <asm/errno.h>
#include <asm/thread_info.h>
#include <asm/asm-offsets.h>
#include <asm/page.h>
#include <asm/pgtable.h>
;; functions exported from this file
.globl system_call
.globl ret_from_intr
.globl ret_from_fork
.globl ret_from_kernel_thread
.globl resume
.globl multiple_interrupt
.globl hwbreakpoint
.globl IRQ1_interrupt
.globl spurious_interrupt
.globl hw_bp_trigs
.globl mmu_bus_fault
.globl do_sigtrap
.globl gdb_handle_breakpoint
.globl sys_call_table
;; below are various parts of system_call which are not in the fast-path
#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
move.d [$r0+TI_preempt_count], $r10 ; Preemption disabled?
bne _Rexit
nop
_need_resched:
move.d [$r0+TI_flags], $r10
btstq TIF_NEED_RESCHED, $r10 ; Check if need_resched is set
bpl _Rexit
nop
; Ok, lets's do some preemptive kernel scheduling
jsr preempt_schedule_irq
; 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
ba ret_from_sys_call
nop
ret_from_kernel_thread:
jsr schedule_tail
move.d $r2, $r10 ; argument is here
jsr $r1 ; call the payload
moveq 0, $r9 ; no syscall restarts, TYVM...
ba ret_from_sys_call
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
;; we cannot simply test $dccr, because that does not necessarily
;; reflect what mode we'll return into.
move.d [$sp + PT_dccr], $r0; regs->dccr
btstq 8, $r0 ; U-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
move.d [$r0+TI_flags], $r10 ; current->work
and.d _TIF_WORK_MASK, $r10 ; is there any work to be done on return
bne _work_pending
nop
ba _Rexit
nop
;; The system_call is called by a BREAK instruction, which works like
;; an interrupt call but it stores the return PC in BRP instead of IRP.
;; Since we dont really want to have two epilogues (one for system calls
;; and one for interrupts) we push the contents of BRP instead of IRP in the
;; system call prologue, to make it look like an ordinary interrupt on the
;; stackframe.
;;
;; Since we can't have system calls inside interrupts, it should not matter
;; that we don't stack IRP.
;;
;; In 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
move $brp,[$sp=$sp-16]; instruction pointer and room for a fake SBFS frame
push $srp
push $dccr
push $mof
subq 14*4, $sp ; make room for r0-r13
movem $r13, [$sp] ; push r0-r13
push $r10 ; push orig_r10
clear.d [$sp=$sp-4] ; frametype == 0, normal stackframe
movs.w -ENOSYS, $r0
move.d $r0, [$sp+PT_r10] ; put the default return value in r10 in the frame
;; check if this process is syscall-traced
movs.w -8192, $r0 ; THREAD_SIZE == 8192
and.d $sp, $r0
move.d [$r0+TI_flags], $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
bcc ret_from_sys_call
lslq 2, $r9 ; multiply by 4, in the delay slot
;; as a bonus 7th parameter, we give the location on the stack
;; of the register structure itself. some syscalls need this.
push $sp
;; the parameter carrying registers r10, r11, r12 and 13 are intact.
;; the fifth and sixth parameters (if any) was in mof and srp
;; respectively, and we need to put them on the stack.
push $srp
push $mof
jsr [$r9+sys_call_table] ; actually do the system call
addq 3*4, $sp ; pop the mof, srp and regs parameters
move.d $r10, [$sp+PT_r10] ; save the return value
moveq 1, $r9 ; "parameter" to ret_from_sys_call to show it was a sys call
;; fall through into ret_from_sys_call to return
ret_from_sys_call:
;; r9 is a parameter - if >=1 we came from a syscall, if 0, from an irq
;; get the current task-struct pointer (see top for defs)
movs.w -8192, $r0 ; THREAD_SIZE == 8192
and.d $sp, $r0
di ; make sure need_resched and sigpending don't change
move.d [$r0+TI_flags],$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
pop $r10 ; frametype
bne _RBFexit ; was not CRIS_FRAME_NORMAL, handle otherwise
addq 4, $sp ; skip orig_r10, in delayslot
movem [$sp+], $r13 ; registers r0-r13
pop $mof ; multiply overflow register
pop $dccr ; condition codes
pop $srp ; subroutine return pointer
;; now we have a 4-word SBFS frame which we do not want to restore
;; using RBF since it was not stacked with SBFS. instead we would like to
;; just get the PC value to restart it with, and skip the rest of
;; the frame.
;; Also notice that it's important to use instructions here that
;; keep the interrupts disabled (since we've already popped DCCR)
move [$sp=$sp+16], $p8; pop the SBFS frame from the sp
jmpu [$sp-16] ; return through the irp field in the sbfs frame
_RBFexit:
movem [$sp+], $r13 ; registers r0-r13, in delay slot
pop $mof ; multiply overflow register
pop $dccr ; condition codes
pop $srp ; subroutine return pointer
rbf [$sp+] ; return by popping the CPU status
;; 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
move.d [$r0+TI_flags], $r1
btstq TIF_SYSCALL_TRACE, $r1
bpl _work_pending
nop
ei
move.d $r9, $r1 ; preserve r9
jsr do_syscall_trace
move.d $r1, $r9
ba _resume_userspace
nop
_work_pending:
move.d [$r0+TI_flags], $r1
btstq TIF_NEED_RESCHED, $r1
bpl _work_notifysig ; was neither trace nor sched, must be signal/notify
nop
_work_resched:
move.d $r9, $r1 ; preserve r9
jsr schedule
move.d $r1, $r9
di
move.d [$r0+TI_flags], $r1
and.d _TIF_WORK_MASK, $r1; ignore the syscall 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
move.d $r9, $r10 ; do_notify_resume syscall/irq param
move.d $sp, $r11 ; the regs param
move.d $r1, $r12 ; the thread_info_flags parameter
jsr do_notify_resume
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
;; 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
move.d [$sp+PT_r9], $r9
move.d [$sp+PT_orig_r10], $r10 ; PT_r10 is already filled with -ENOSYS.
move.d [$sp+PT_r11], $r11
move.d [$sp+PT_r12], $r12
move.d [$sp+PT_r13], $r13
move [$sp+PT_mof], $mof
move [$sp+PT_srp], $srp
ba _syscall_traced
nop
;; resume performs the actual task-switching, by switching stack pointers
;; input arguments: r10 = prev, r11 = next, r12 = thread offset in task struct
;; returns old current in r10
;;
;; TODO: see the i386 version. The switch_to which calls resume in our version
;; could really be an inline asm of this.
resume:
push $srp ; we keep the old/new PC on the stack
add.d $r12, $r10 ; r10 = current tasks tss
move $dccr, [$r10+THREAD_dccr]; save irq enable state
di
move $usp, [$r10+ THREAD_usp] ; 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.
move.d $sp, [$r10+THREAD_ksp] ; save the kernel stack pointer for the old task
move.d $sp, $r10 ; return last running task in r10
and.d -8192, $r10 ; get thread_info from stackpointer
move.d [$r10+TI_task], $r10 ; get task
add.d $r12, $r11 ; find the new tasks tss
move.d [$r11+THREAD_ksp], $sp ; switch into the new stackframe by restoring kernel sp
movem [$sp+], $r9 ; restore non-scratch registers and R9.
move [$r11+THREAD_usp], $usp ; restore user-mode stackpointer
move [$r11+THREAD_dccr], $dccr ; restore irq enable status
jump [$sp+] ; restore PC
;; This is the MMU bus fault handler.
;; It needs to stack the CPU status and overall is different
;; from the other interrupt handlers.
mmu_bus_fault:
;; For refills we try to do a quick page table lookup. If it is
;; a real fault we let the mm subsystem handle it.
;; the first longword in the sbfs frame was the interrupted PC
;; which fits nicely with the "IRP" slot in pt_regs normally used to
;; contain the return address. used by Oops to print kernel errors.
sbfs [$sp=$sp-16] ; push the internal CPU status
push $dccr
di
subq 2*4, $sp
movem $r1, [$sp]
move.d [R_MMU_CAUSE], $r1
;; ETRAX 100LX TR89 bugfix: if the second half of an unaligned
;; write causes a MMU-fault, it will not be restarted correctly.
;; This could happen if a write crosses a page-boundary and the
;; second page is not yet COW'ed or even loaded. The workaround
;; is to clear the unaligned bit in the CPU status record, so
;; that the CPU will rerun both the first and second halves of
;; the instruction. This will not have any sideeffects unless
;; the first half goes to any device or memory that can't be
;; written twice, and which is mapped through the MMU.
;;
;; We only need to do this for writes.
btstq 8, $r1 ; Write access?
bpl 1f
nop
move.d [$sp+16], $r0 ; Clear unaligned bit in csrinstr
and.d ~(1<<5), $r0
move.d $r0, [$sp+16]
1: btstq 12, $r1 ; Refill?
bpl 2f
lsrq 24, $r1 ; Get PGD index (bit 24-31)
move.d [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 PGD (bit 13-23)
move.d [$r0+$r1.d], $r1 ; Get PTE
beq 2f
nop
;; Store in TLB
move.d $r1, [R_TLB_LO]
;; Return
movem [$sp+], $r1
pop $dccr
rbf [$sp+] ; return by popping the CPU status
2: ; PMD or PTE missing, let the mm subsystem fix it up.
movem [$sp+], $r1
pop $dccr
; Ok, not that easy, pass it on to the mm subsystem
; The MMU status record is now on the stack
push $srp ; make a stackframe similar to pt_regs
push $dccr
push $mof
di
subq 14*4, $sp
movem $r13, [$sp]
push $r10 ; dummy orig_r10
moveq 1, $r10
push $r10 ; frametype == 1, BUSFAULT frame type
move.d $sp, $r10 ; pt_regs argument to handle_mmu_bus_fault
jsr handle_mmu_bus_fault ; in arch/cris/arch-v10/mm/fault.c
;; now we need to return through the normal path, we cannot just
;; do the RBFexit since we might have killed off the running
;; process due to a SEGV, scheduled due to a page blocking or
;; whatever.
moveq 0, $r9 ; busfault is equivalent to an irq
ba ret_from_intr
nop
;; special handlers for breakpoint and NMI
hwbreakpoint:
push $dccr
di
push $r10
push $r11
move.d [hw_bp_trig_ptr],$r10
move $brp,$r11
move.d $r11,[$r10+]
move.d $r10,[hw_bp_trig_ptr]
1: pop $r11
pop $r10
pop $dccr
retb
nop
IRQ1_interrupt:
;; 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
push $dccr
push $mof
di
subq 14*4, $sp
movem $r13, [$sp]
push $r10 ; push orig_r10
clear.d [$sp=$sp-4] ; frametype == 0, normal frame
;; If there is a glitch on the NMI pin shorter than ~100ns
;; (i.e. non-active by the time we get here) then the nmi_pin bit
;; in R_IRQ_MASK0_RD will already be cleared. The watchdog_nmi bit
;; is cleared by us however (when feeding the watchdog), which is why
;; we use that bit to determine what brought us here.
move.d [R_IRQ_MASK0_RD], $r1 ; External NMI or watchdog?
and.d (1<<30), $r1
bne wdog
move.d $sp, $r10
jsr handle_nmi
setf m ; Enable NMI again
ba _Rexit ; Return the standard way
nop
wdog:
#if defined(CONFIG_ETRAX_WATCHDOG)
;; 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.
.comm cause_of_death, 4 ;; Don't declare this anywhere.
move.d [cause_of_death], $r10
cmp.d 0xbedead, $r10
_killed_by_death:
beq _killed_by_death
nop
;; We'll see this in ksymoops dumps.
Watchdog_bite:
#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
;; We just restart the watchdog here to be sure we dont get
;; hit while printing the watchdogmsg below
;; This restart is compatible with the rest of the C-code, so
;; the C-code can keep restarting the watchdog after this point.
;; The non-NICE_DOGGY code below though, disables the possibility
;; to restart since it changes the watchdog key, to avoid any
;; buggy loops etc. keeping the watchdog alive after this.
jsr reset_watchdog
#else
;; We need to extend the 3.3ms after the NMI at watchdog bite, so we have
;; time for an oops-dump over a 115k2 serial wire. Another 100ms should do.
;; Change the watchdog key to an arbitrary 3-bit value and restart the
;; watchdog.
#define WD_INIT 2
moveq IO_FIELD (R_WATCHDOG, key, WD_INIT), $r10
move.d R_WATCHDOG, $r11
move.d $r10, [$r11]
moveq IO_FIELD (R_WATCHDOG, key, \
IO_EXTRACT (R_WATCHDOG, key, \
IO_MASK (R_WATCHDOG, key)) \
^ WD_INIT) \
| IO_STATE (R_WATCHDOG, enable, start), $r10
move.d $r10, [$r11]
#endif
;; Note that we don't do "setf m" here (or after two necessary NOPs),
;; since *not* doing that saves us from re-entrancy checks. We don't want
;; to get here again due to possible subsequent NMIs; we want the watchdog
;; to reset us.
move.d _watchdogmsg,$r10
jsr printk
move.d $sp, $r10
jsr watchdog_bite_hook
;; This nop is here so we see the "Watchdog_bite" label in ksymoops dumps
;; rather than "spurious_interrupt".
nop
;; At this point we drop down into spurious_interrupt, which will do a
;; hard reset.
.section .rodata,"a"
_watchdogmsg:
.ascii "Oops: bitten by watchdog\n\0"
.previous
#endif /* CONFIG_ETRAX_WATCHDOG */
spurious_interrupt:
di
jump hard_reset_now
;; this handles the case when multiple interrupts arrive at the same time
;; we jump to the first set interrupt bit in a priority fashion
;; the hardware will call the unserved interrupts after the handler finishes
multiple_interrupt:
;; this prologue MUST match the one in irq.h and the struct in ptregs.h!!!
move $irp,[$sp=$sp-16]; instruction pointer and room for a fake SBFS frame
push $srp
push $dccr
push $mof
di
subq 14*4, $sp
movem $r13, [$sp]
push $r10 ; push orig_r10
clear.d [$sp=$sp-4] ; frametype == 0, normal frame
move.d $sp, $r10
jsr do_multiple_IRQ
jump ret_from_intr
do_sigtrap:
;;
;; SIGTRAP the process that executed the break instruction.
;; Make a frame that Rexit in entry.S expects.
;;
move $brp, [$sp=$sp-16] ; Push BRP while faking a cpu status record.
push $srp ; Push subroutine return pointer.
push $dccr ; Push condition codes.
push $mof ; Push multiply overflow reg.
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.
push $r10 ; Push orig_r10.
clear.d [$sp=$sp-4] ; Frametype - this is a normal stackframe.
movs.w -8192,$r9 ; THREAD_SIZE == 8192
and.d $sp, $r9
move.d [$r9+TI_task], $r10
move.d [$r10+TASK_pid], $r10 ; current->pid as arg1.
moveq 5, $r11 ; SIGTRAP as arg2.
jsr sys_kill
jump ret_from_intr ; Use the return routine for interrupts.
gdb_handle_breakpoint:
push $dccr
push $r0
#ifdef CONFIG_ETRAX_KGDB
move $dccr, $r0 ; U-flag not affected by previous insns.
btstq 8, $r0 ; Test the U-flag.
bmi _ugdb_handle_breakpoint ; Go to user mode debugging.
nop ; Empty delay slot (cannot pop r0 here).
pop $r0 ; Restore r0.
ba kgdb_handle_breakpoint ; Go to kernel debugging.
pop $dccr ; Restore dccr in delay slot.
#endif
_ugdb_handle_breakpoint:
move $brp, $r0 ; Use r0 temporarily for calculation.
subq 2, $r0 ; Set to address of previous instruction.
move $r0, $brp
pop $r0 ; Restore r0.
ba do_sigtrap ; SIGTRAP the offending process.
pop $dccr ; Restore dccr in delay slot.
.data
hw_bp_trigs:
.space 64*4
hw_bp_trig_ptr:
.dword hw_bp_trigs
.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 sys_old_readdir
.long sys_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 /* 110 */ /* 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_ni_syscall /* old 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 /* mmap_pgoff */
.long sys_truncate64
.long sys_ftruncate64
.long sys_stat64 /* 195 */
.long sys_lstat64
.long sys_fstat64
.long sys_lchown
.long sys_getuid
.long sys_getgid /* 200 */
.long sys_geteuid
.long sys_getegid
.long sys_setreuid
.long sys_setregid
.long sys_getgroups /* 205 */
.long sys_setgroups
.long sys_fchown
.long sys_setresuid
.long sys_getresuid
.long sys_setresgid /* 210 */
.long sys_getresgid
.long sys_chown
.long sys_setuid
.long sys_setgid
.long sys_setfsuid /* 215 */
.long sys_setfsgid
.long sys_pivot_root
.long sys_mincore
.long sys_madvise
.long sys_getdents64 /* 220 */
.long sys_fcntl64
.long sys_ni_syscall /* reserved for TUX */
.long sys_ni_syscall
.long sys_gettid
.long sys_readahead /* 225 */
.long sys_setxattr
.long sys_lsetxattr
.long sys_fsetxattr
.long sys_getxattr
.long sys_lgetxattr /* 230 */
.long sys_fgetxattr
.long sys_listxattr
.long sys_llistxattr
.long sys_flistxattr
.long sys_removexattr /* 235 */
.long sys_lremovexattr
.long sys_fremovexattr
.long sys_tkill
.long sys_sendfile64
.long sys_futex /* 240 */
.long sys_sched_setaffinity
.long sys_sched_getaffinity
.long sys_ni_syscall /* sys_set_thread_area */
.long sys_ni_syscall /* sys_get_thread_area */
.long sys_io_setup /* 245 */
.long sys_io_destroy
.long sys_io_getevents
.long sys_io_submit
.long sys_io_cancel
.long sys_fadvise64 /* 250 */
.long sys_ni_syscall
.long sys_exit_group
.long sys_lookup_dcookie
.long sys_epoll_create
.long sys_epoll_ctl /* 255 */
.long sys_epoll_wait
.long sys_remap_file_pages
.long sys_set_tid_address
.long sys_timer_create
.long sys_timer_settime /* 260 */
.long sys_timer_gettime
.long sys_timer_getoverrun
.long sys_timer_delete
.long sys_clock_settime
.long sys_clock_gettime /* 265 */
.long sys_clock_getres
.long sys_clock_nanosleep
.long sys_statfs64
.long sys_fstatfs64
.long sys_tgkill /* 270 */
.long sys_utimes
.long sys_fadvise64_64
.long sys_ni_syscall /* sys_vserver */
.long sys_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
.long sys_waitid
.long sys_ni_syscall /* 285 */ /* available */
.long sys_add_key
.long sys_request_key
.long sys_keyctl
.long sys_ioprio_set
.long sys_ioprio_get /* 290 */
.long sys_inotify_init
.long sys_inotify_add_watch
.long sys_inotify_rm_watch
.long sys_migrate_pages
.long sys_openat /* 295 */
.long sys_mkdirat
.long sys_mknodat
.long sys_fchownat
.long sys_futimesat
.long sys_fstatat64 /* 300 */
.long sys_unlinkat
.long sys_renameat
.long sys_linkat
.long sys_symlinkat
.long sys_readlinkat /* 305 */
.long sys_fchmodat
.long sys_faccessat
.long sys_pselect6
.long sys_ppoll
.long sys_unshare /* 310 */
.long sys_set_robust_list
.long sys_get_robust_list
.long sys_splice
.long sys_sync_file_range
.long sys_tee /* 315 */
.long sys_vmsplice
.long sys_move_pages
.long sys_getcpu
.long sys_epoll_pwait
.long sys_utimensat /* 320 */
.long sys_signalfd
.long sys_timerfd_create
.long sys_eventfd
.long sys_fallocate
.long sys_timerfd_settime /* 325 */
.long sys_timerfd_gettime
.long sys_signalfd4
.long sys_eventfd2
.long sys_epoll_create1
.long sys_dup3 /* 330 */
.long sys_pipe2
.long sys_inotify_init1
.long sys_preadv
.long sys_pwritev
.long sys_setns /* 335 */
.long sys_name_to_handle_at
.long sys_open_by_handle_at
.long sys_rt_tgsigqueueinfo
.long sys_perf_event_open
.long sys_recvmmsg /* 340 */
.long sys_accept4
.long sys_fanotify_init
.long sys_fanotify_mark
.long sys_prlimit64
.long sys_clock_adjtime /* 345 */
.long sys_syncfs
.long sys_sendmmsg
.long sys_process_vm_readv
.long sys_process_vm_writev
.long sys_kcmp /* 350 */
.long sys_finit_module
.long sys_sched_setattr
.long sys_sched_getattr
.long sys_renameat2
.long sys_seccomp /* 355 */
.long sys_getrandom
.long sys_memfd_create
.long sys_bpf
.long sys_execveat
/*
* 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

@ -1,835 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/cris/kernel/fasttimer.c
*
* Fast timers for ETRAX100/ETRAX100LX
*
* Copyright (C) 2000-2007 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/mm.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/delay.h>
#include <arch/svinto.h>
#include <asm/fasttimer.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#define DEBUG_LOG_INCLUDED
#define FAST_TIMER_LOG
/* #define FAST_TIMER_TEST */
#define FAST_TIMER_SANITY_CHECKS
#ifdef FAST_TIMER_SANITY_CHECKS
static int sanity_failed;
#endif
#define D1(x)
#define D2(x)
#define DP(x)
static unsigned int fast_timer_running;
static unsigned int fast_timers_added;
static unsigned int fast_timers_started;
static unsigned int fast_timers_expired;
static unsigned int fast_timers_deleted;
static unsigned int fast_timer_is_init;
static unsigned int fast_timer_ints;
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 unsigned int debug_log_cnt;
static unsigned int debug_log_cnt_wrapped;
#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
/* The frequencies for index = clkselx number in R_TIMER_CTRL */
#define NUM_TIMER_FREQ 15
#define MAX_USABLE_TIMER_FREQ 7
#define MAX_DELAY_US 853333L
const unsigned long timer_freq_100[NUM_TIMER_FREQ] =
{
3, /* 0 3333 - 853333 us */
6, /* 1 1666 - 426666 us */
12, /* 2 833 - 213333 us */
24, /* 3 416 - 106666 us */
48, /* 4 208 - 53333 us */
96, /* 5 104 - 26666 us */
192, /* 6 52 - 13333 us */
384, /* 7 26 - 6666 us */
576,
1152,
2304,
4608,
9216,
18432,
62500,
/* 15 = cascade */
};
#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_freq_settings[NUM_TIMER_STATS];
int timer_delay_settings[NUM_TIMER_STATS];
/* Not true gettimeofday, only checks the jiffies (uptime) + useconds */
inline void do_gettimeofday_fast(struct fasttime_t *tv)
{
tv->tv_jiff = jiffies;
tv->tv_usec = GET_JIFFIES_USEC();
}
inline int fasttime_cmp(struct fasttime_t *t0, struct fasttime_t *t1)
{
/* Compare jiffies. Takes care of wrapping */
if (time_before(t0->tv_jiff, t1->tv_jiff))
return -1;
else if (time_after(t0->tv_jiff, t1->tv_jiff))
return 1;
/* Compare us */
if (t0->tv_usec < t1->tv_usec)
return -1;
else if (t0->tv_usec > t1->tv_usec)
return 1;
return 0;
}
inline void start_timer1(unsigned long delay_us)
{
int freq_index = 0; /* This is the lowest resolution */
unsigned long upper_limit = MAX_DELAY_US;
unsigned long div;
/* Start/Restart the timer to the new shorter value */
/* t = 1/freq = 1/19200 = 53us
* T=div*t, div = T/t = delay_us*freq/1000000
*/
#if 1 /* Adaptive timer settings */
while (delay_us < upper_limit && freq_index < MAX_USABLE_TIMER_FREQ)
{
freq_index++;
upper_limit >>= 1; /* Divide by 2 using shift */
}
if (freq_index > 0)
{
freq_index--;
}
#else
freq_index = 6;
#endif
div = delay_us * timer_freq_100[freq_index]/10000;
if (div < 2)
{
/* Maybe increase timer freq? */
div = 2;
}
if (div > 255)
{
div = 0; /* This means 256, the max the timer takes */
/* If a longer timeout than the timer can handle is used,
* then we must restart it when it goes off.
*/
}
timer_div_settings[fast_timers_started % NUM_TIMER_STATS] = div;
timer_freq_settings[fast_timers_started % NUM_TIMER_STATS] = freq_index;
timer_delay_settings[fast_timers_started % NUM_TIMER_STATS] = delay_us;
D1(printk(KERN_DEBUG "start_timer1 : %d us freq: %i div: %i\n",
delay_us, freq_index, div));
/* Clear timer1 irq */
*R_IRQ_MASK0_CLR = IO_STATE(R_IRQ_MASK0_CLR, timer1, clr);
/* Set timer values */
*R_TIMER_CTRL = r_timer_ctrl_shadow =
(r_timer_ctrl_shadow &
~IO_MASK(R_TIMER_CTRL, timerdiv1) &
~IO_MASK(R_TIMER_CTRL, tm1) &
~IO_MASK(R_TIMER_CTRL, clksel1)) |
IO_FIELD(R_TIMER_CTRL, timerdiv1, div) |
IO_STATE(R_TIMER_CTRL, tm1, stop_ld) |
IO_FIELD(R_TIMER_CTRL, clksel1, freq_index ); /* 6=c19k2Hz */
/* Ack interrupt */
*R_TIMER_CTRL = r_timer_ctrl_shadow |
IO_STATE(R_TIMER_CTRL, i1, clr);
/* Start timer */
*R_TIMER_CTRL = r_timer_ctrl_shadow =
(r_timer_ctrl_shadow & ~IO_MASK(R_TIMER_CTRL, tm1)) |
IO_STATE(R_TIMER_CTRL, tm1, run);
/* Enable timer1 irq */
*R_IRQ_MASK0_SET = IO_STATE(R_IRQ_MASK0_SET, timer1, set);
fast_timers_started++;
fast_timer_running = 1;
}
/* 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;
#ifdef FAST_TIMER_SANITY_CHECKS
/* Check so this is not in the list already... */
while (tmp != NULL) {
if (tmp == t) {
printk(KERN_WARNING "timer name: %s data: "
"0x%08lX already in list!\n", name, data);
sanity_failed++;
goto done;
} else
tmp = tmp->next;
}
tmp = fast_timer_list;
#endif
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_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ;
if (t->tv_expires.tv_usec > 1000000)
{
t->tv_expires.tv_usec -= 1000000;
t->tv_expires.tv_jiff += HZ;
}
#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 || fasttime_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_timer1(delay_us);
} else {
/* Put in correct place in list */
while (tmp->next && fasttime_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));
done:
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 1 interrupt handler */
static irqreturn_t
timer1_handler(int irq, void *dev_id)
{
struct fast_timer *t;
unsigned long flags;
/* We keep interrupts disabled not only when we modify the
* fast timer list, but any time we hold a reference to a
* timer in the list, since del_fast_timer may be called
* from (another) interrupt context. Thus, the only time
* when interrupts are enabled is when calling the timer
* callback function.
*/
local_irq_save(flags);
/* Clear timer1 irq */
*R_IRQ_MASK0_CLR = IO_STATE(R_IRQ_MASK0_CLR, timer1, clr);
/* First stop timer, then ack interrupt */
/* Stop timer */
*R_TIMER_CTRL = r_timer_ctrl_shadow =
(r_timer_ctrl_shadow & ~IO_MASK(R_TIMER_CTRL, tm1)) |
IO_STATE(R_TIMER_CTRL, tm1, stop_ld);
/* Ack interrupt */
*R_TIMER_CTRL = r_timer_ctrl_shadow | IO_STATE(R_TIMER_CTRL, i1, clr);
fast_timer_running = 0;
fast_timer_ints++;
t = fast_timer_list;
while (t)
{
struct fasttime_t tv;
fast_timer_function_type *f;
unsigned long d;
/* Has it really expired? */
do_gettimeofday_fast(&tv);
D1(printk(KERN_DEBUG "t: %is %06ius\n",
tv.tv_jiff, tv.tv_usec));
if (fasttime_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 */
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;
/* Save function callback data before enabling
* interrupts, since the timer may be removed and
* we don't know how it was allocated
* (e.g. ->function and ->data may become overwritten
* after deletion if the timer was stack-allocated).
*/
f = t->function;
d = t->data;
if (f != NULL) {
/* Run callback with interrupts enabled. */
local_irq_restore(flags);
f(d);
local_irq_save(flags);
} else
DEBUG_LOG("!timer1 %i function==NULL!\n", fast_timer_ints);
}
else
{
/* Timer is to early, let's set it again using the normal routines */
D1(printk(".\n"));
}
if ((t = fast_timer_list) != NULL)
{
/* Start next timer.. */
long us = 0;
struct fasttime_t tv;
do_gettimeofday_fast(&tv);
/* time_after_eq takes care of wrapping */
if (time_after_eq(t->tv_expires.tv_jiff, tv.tv_jiff))
us = ((t->tv_expires.tv_jiff - tv.tv_jiff) *
1000000 / HZ + 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_timer1(us);
}
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("t1 stop!\n"));
}
return IRQ_HANDLED;
}
static void wake_up_func(unsigned long data)
{
wait_queue_head_t *sleep_wait_p = (wait_queue_head_t *)data;
wake_up(sleep_wait_p);
}
/* Useful API */
void schedule_usleep(unsigned long us)
{
struct fast_timer t;
wait_queue_head_t sleep_wait;
init_waitqueue_head(&sleep_wait);
D1(printk("schedule_usleep(%d)\n", us));
start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us,
"usleep");
/* Uninterruptible sleep on the fast timer. (The condition is somewhat
* redundant since the timer is what wakes us up.) */
wait_event(sleep_wait, !fast_timer_pending(&t));
D1(printk("done schedule_usleep(%d)\n", us));
}
#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_show(struct seq_file *m, void *v)
{
unsigned long flags;
int i = 0;
int num_to_show;
struct fasttime_t tv;
struct fast_timer *t, *nextt;
do_gettimeofday_fast(&tv);
seq_printf(m, "Fast timers added: %i\n", fast_timers_added);
seq_printf(m, "Fast timers started: %i\n", fast_timers_started);
seq_printf(m, "Fast timer interrupts: %i\n", fast_timer_ints);
seq_printf(m, "Fast timers expired: %i\n", fast_timers_expired);
seq_printf(m, "Fast timers deleted: %i\n", fast_timers_deleted);
seq_printf(m, "Fast timer running: %s\n",
fast_timer_running ? "yes" : "no");
seq_printf(m, "Current time: %lu.%06lu\n",
(unsigned long)tv.tv_jiff,
(unsigned long)tv.tv_usec);
#ifdef FAST_TIMER_SANITY_CHECKS
seq_printf(m, "Sanity failed: %i\n", sanity_failed);
#endif
seq_putc(m, '\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) {
seq_printf(m, debug_log_string[i], debug_log_value[i]);
if (seq_has_overflowed(m))
return 0;
i = (i+1) % DEBUG_LOG_MAX;
}
}
seq_putc(m, '\n');
#endif
num_to_show = (fast_timers_started < NUM_TIMER_STATS ? fast_timers_started:
NUM_TIMER_STATS);
seq_printf(m, "Timers started: %i\n", fast_timers_started);
for (i = 0; i < num_to_show; i++) {
int cur = (fast_timers_started - i - 1) % NUM_TIMER_STATS;
#if 1 //ndef FAST_TIMER_LOG
seq_printf(m, "div: %i freq: %i delay: %i\n",
timer_div_settings[cur],
timer_freq_settings[cur],
timer_delay_settings[cur]);
#endif
#ifdef FAST_TIMER_LOG
t = &timer_started_log[cur];
seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 0x%08lX\n",
t->name,
(unsigned long)t->tv_set.tv_jiff,
(unsigned long)t->tv_set.tv_usec,
(unsigned long)t->tv_expires.tv_jiff,
(unsigned long)t->tv_expires.tv_usec,
t->delay_us,
t->data);
if (seq_has_overflowed(m))
return 0;
#endif
}
seq_putc(m, '\n');
#ifdef FAST_TIMER_LOG
num_to_show = (fast_timers_added < NUM_TIMER_STATS ? fast_timers_added:
NUM_TIMER_STATS);
seq_printf(m, "Timers added: %i\n", fast_timers_added);
for (i = 0; i < num_to_show; i++) {
t = &timer_added_log[(fast_timers_added - i - 1) % NUM_TIMER_STATS];
seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 0x%08lX\n",
t->name,
(unsigned long)t->tv_set.tv_jiff,
(unsigned long)t->tv_set.tv_usec,
(unsigned long)t->tv_expires.tv_jiff,
(unsigned long)t->tv_expires.tv_usec,
t->delay_us,
t->data);
if (seq_has_overflowed(m))
return 0;
}
seq_putc(m, '\n');
num_to_show = (fast_timers_expired < NUM_TIMER_STATS ? fast_timers_expired:
NUM_TIMER_STATS);
seq_printf(m, "Timers expired: %i\n", fast_timers_expired);
for (i = 0; i < num_to_show; i++) {
t = &timer_expired_log[(fast_timers_expired - i - 1) % NUM_TIMER_STATS];
seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 0x%08lX\n",
t->name,
(unsigned long)t->tv_set.tv_jiff,
(unsigned long)t->tv_set.tv_usec,
(unsigned long)t->tv_expires.tv_jiff,
(unsigned long)t->tv_expires.tv_usec,
t->delay_us,
t->data);
if (seq_has_overflowed(m))
return 0;
}
seq_putc(m, '\n');
#endif
seq_puts(m, "Active timers:\n");
local_irq_save(flags);
t = fast_timer_list;
while (t) {
nextt = t->next;
local_irq_restore(flags);
seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 0x%08lX\n",
t->name,
(unsigned long)t->tv_set.tv_jiff,
(unsigned long)t->tv_set.tv_usec,
(unsigned long)t->tv_expires.tv_jiff,
(unsigned long)t->tv_expires.tv_usec,
t->delay_us,
t->data);
if (seq_has_overflowed(m))
return 0;
local_irq_save(flags);
if (t->next != nextt)
printk(KERN_WARNING "timer removed!\n");
t = nextt;
}
local_irq_restore(flags);
return 0;
}
static int proc_fasttimer_open(struct inode *inode, struct file *file)
{
return single_open_size(file, proc_fasttimer_show, PDE_DATA(inode), BIG_BUF_SIZE);
}
static const struct file_operations proc_fasttimer_fops = {
.open = proc_fasttimer_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#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 fasttime_t 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 fasttime_t 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(KERN_DEBUG "fast_timer_test() %is %06i\n",
tv.tv_jiff, 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(KERN_DEBUG "%i.%i %i.%i %i.%i %i.%i %i.%i\n",
tv_exp[j].tv_jiff, tv_exp[j].tv_usec,
tv_exp[j+1].tv_jiff, tv_exp[j+1].tv_usec,
tv_exp[j+2].tv_jiff, tv_exp[j+2].tv_usec,
tv_exp[j+3].tv_jiff, tv_exp[j+3].tv_usec,
tv_exp[j+4].tv_jiff, 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(KERN_DEBUG "Timers started %is %06i\n",
tv0.tv_jiff, tv0.tv_usec);
printk(KERN_DEBUG "Timers started at %is %06i\n",
tv1.tv_jiff, tv1.tv_usec);
printk(KERN_DEBUG "Timers done %is %06i\n",
tv2.tv_jiff, 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_jiff,
t->tv_set.tv_usec,
t->tv_expires.tv_jiff,
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_jiff,
tv_exp[j].tv_usec,
exp_num[j],
(tv_exp[j].tv_jiff - t->tv_expires.tv_jiff) *
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
int fast_timer_init(void)
{
/* For some reason, request_irq() hangs when called froom time_init() */
if (!fast_timer_is_init)
{
#if 0 && defined(FAST_TIMER_TEST)
int i;
#endif
printk(KERN_INFO "fast_timer_init()\n");
#if 0 && defined(FAST_TIMER_TEST)
for (i = 0; i <= TIMER0_DIV; i++)
{
/* We must be careful not to get overflow... */
printk("%3i %6u\n", i, timer0_value_us[i]);
}
#endif
#ifdef CONFIG_PROC_FS
proc_create("fasttimer", 0, NULL, &proc_fasttimer_fops);
#endif /* PROC_FS */
if(request_irq(TIMER1_IRQ_NBR, timer1_handler, 0,
"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
}
return 0;
}
__initcall(fast_timer_init);

View File

@ -1,620 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Head of the kernel - alter with care
*
* Copyright (C) 2000, 2001, 2010 Axis Communications AB
*
*/
#include <linux/init.h>
#define ASSEMBLER_MACROS_ONLY
/* The IO_* macros use the ## token concatenation operator, so
-traditional must not be used when assembling this file. */
#include <arch/sv_addr_ag.h>
#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)
;; exported symbols
.globl etrax_irv
.globl romfs_start
.globl romfs_length
.globl romfs_in_flash
.globl swapper_pg_dir
__HEAD
;; This is the entry point of the kernel. We are in supervisor mode.
;; 0x00000000 if Flash, 0x40004000 if DRAM
;; since etrax actually starts at address 2 when booting from flash, we
;; put a nop (2 bytes) here first so we dont accidentally skip the di
;;
;; NOTICE! The 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 until read.
nop
di
;; First setup the kseg_c mapping from where the kernel is linked
;; to 0x40000000 (where the actual DRAM resides) otherwise
;; we cannot do very much! See arch/cris/README.mm
;;
;; Notice that since we're potentially running at 0x00 or 0x40 right now,
;; we will get a fault as soon as we enable the MMU if we dont
;; temporarily map those segments linearily.
;;
;; Due to a bug in Etrax-100 LX version 1 we need to map the memory
;; slightly different. The bug is that you can't remap bit 31 of
;; an address. Though we can check the version register for
;; whether the bug is present, some constants would then have to
;; be variables, so we don't. The drawback is that you can "only" map
;; 1G per process with CONFIG_CRIS_LOW_MAP.
#ifdef CONFIG_CRIS_LOW_MAP
; kseg mappings, temporary map of 0xc0->0x40
move.d IO_FIELD (R_MMU_KBASE_HI, base_c, 4) \
| IO_FIELD (R_MMU_KBASE_HI, base_b, 0xb) \
| IO_FIELD (R_MMU_KBASE_HI, base_9, 9) \
| IO_FIELD (R_MMU_KBASE_HI, base_8, 8), $r0
move.d $r0, [R_MMU_KBASE_HI]
; temporary map of 0x40->0x40 and 0x60->0x40
move.d IO_FIELD (R_MMU_KBASE_LO, base_6, 4) \
| IO_FIELD (R_MMU_KBASE_LO, base_4, 4), $r0
move.d $r0, [R_MMU_KBASE_LO]
; mmu enable, segs e,c,b,a,6,5,4,0 segment mapped
move.d IO_STATE (R_MMU_CONFIG, mmu_enable, enable) \
| IO_STATE (R_MMU_CONFIG, inv_excp, enable) \
| IO_STATE (R_MMU_CONFIG, acc_excp, enable) \
| IO_STATE (R_MMU_CONFIG, we_excp, enable) \
| IO_STATE (R_MMU_CONFIG, seg_f, page) \
| IO_STATE (R_MMU_CONFIG, seg_e, seg) \
| IO_STATE (R_MMU_CONFIG, seg_d, page) \
| IO_STATE (R_MMU_CONFIG, seg_c, seg) \
| IO_STATE (R_MMU_CONFIG, seg_b, seg) \
| IO_STATE (R_MMU_CONFIG, seg_a, seg) \
| IO_STATE (R_MMU_CONFIG, seg_9, page) \
| IO_STATE (R_MMU_CONFIG, seg_8, page) \
| IO_STATE (R_MMU_CONFIG, seg_7, page) \
| IO_STATE (R_MMU_CONFIG, seg_6, seg) \
| IO_STATE (R_MMU_CONFIG, seg_5, seg) \
| IO_STATE (R_MMU_CONFIG, seg_4, seg) \
| IO_STATE (R_MMU_CONFIG, seg_3, page) \
| IO_STATE (R_MMU_CONFIG, seg_2, page) \
| IO_STATE (R_MMU_CONFIG, seg_1, page) \
| IO_STATE (R_MMU_CONFIG, seg_0, seg), $r0
move.d $r0, [R_MMU_CONFIG]
#else
; kseg mappings
move.d IO_FIELD (R_MMU_KBASE_HI, base_e, 8) \
| IO_FIELD (R_MMU_KBASE_HI, base_c, 4) \
| IO_FIELD (R_MMU_KBASE_HI, base_b, 0xb), $r0
move.d $r0, [R_MMU_KBASE_HI]
; temporary map of 0x40->0x40 and 0x00->0x00
move.d IO_FIELD (R_MMU_KBASE_LO, base_4, 4), $r0
move.d $r0, [R_MMU_KBASE_LO]
; mmu enable, segs f,e,c,b,4,0 segment mapped
move.d IO_STATE (R_MMU_CONFIG, mmu_enable, enable) \
| IO_STATE (R_MMU_CONFIG, inv_excp, enable) \
| IO_STATE (R_MMU_CONFIG, acc_excp, enable) \
| IO_STATE (R_MMU_CONFIG, we_excp, enable) \
| IO_STATE (R_MMU_CONFIG, seg_f, seg) \
| IO_STATE (R_MMU_CONFIG, seg_e, seg) \
| IO_STATE (R_MMU_CONFIG, seg_d, page) \
| IO_STATE (R_MMU_CONFIG, seg_c, seg) \
| IO_STATE (R_MMU_CONFIG, seg_b, seg) \
| IO_STATE (R_MMU_CONFIG, seg_a, page) \
| IO_STATE (R_MMU_CONFIG, seg_9, page) \
| IO_STATE (R_MMU_CONFIG, seg_8, page) \
| IO_STATE (R_MMU_CONFIG, seg_7, page) \
| IO_STATE (R_MMU_CONFIG, seg_6, page) \
| IO_STATE (R_MMU_CONFIG, seg_5, page) \
| IO_STATE (R_MMU_CONFIG, seg_4, seg) \
| IO_STATE (R_MMU_CONFIG, seg_3, page) \
| IO_STATE (R_MMU_CONFIG, seg_2, page) \
| IO_STATE (R_MMU_CONFIG, seg_1, page) \
| IO_STATE (R_MMU_CONFIG, seg_0, seg), $r0
move.d $r0, [R_MMU_CONFIG]
#endif
;; Now we need to sort out the segments and their locations in RAM or
;; Flash. The image in the Flash (or in DRAM) consists of 3 pieces:
;; 1) kernel text, 2) kernel data, 3) ROM filesystem image
;; But the linker has linked the kernel to expect this layout in
;; DRAM memory:
;; 1) kernel text, 2) kernel data, 3) kernel BSS
;; (the location of the ROM filesystem is determined by the krom driver)
;; If we boot this from Flash, we want to keep the ROM filesystem in
;; the flash, we want to copy the text and need to copy the data to DRAM.
;; But if we boot from DRAM, we need to move the ROMFS image
;; from its position after kernel data, to after kernel BSS, BEFORE the
;; kernel starts using the BSS area (since its "overlayed" with the ROMFS)
;;
;; In both cases, we start in un-cached mode, and need to jump into a
;; cached PC after we're done fiddling around with the segments.
;;
;; arch/etrax100/etrax100.ld sets some symbols that define the start
;; and end of each segment.
;; Check if we start from DRAM or FLASH by testing PC
move.d $pc,$r0
and.d 0x7fffffff,$r0 ; get rid of the non-cache bit
cmp.d 0x10000,$r0 ; arbitrary... just something above this code
blo _inflash0
nop
jump _inram ; enter cached ram
;; Jumpgate for branches.
_inflash0:
jump _inflash
;; Put this in a suitable section where we can reclaim storage
;; after init.
__INIT
_inflash:
#ifdef CONFIG_ETRAX_ETHERNET
;; Start MII clock to make sure it is running when tranceiver is reset
move.d START_ETHERNET_CLOCK, $r0
move.d $r0, [R_NETWORK_GEN_CONFIG]
#endif
;; Set up waitstates etc according to kernel configuration.
move.d CONFIG_ETRAX_DEF_R_WAITSTATES, $r0
move.d $r0, [R_WAITSTATES]
move.d CONFIG_ETRAX_DEF_R_BUS_CONFIG, $r0
move.d $r0, [R_BUS_CONFIG]
;; We need to initialze DRAM registers before we start using the DRAM
cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized?
beq _dram_init_finished
nop
#include "../lib/dram_init.S"
_dram_init_finished:
;; Copy text+data to DRAM
;; This is fragile - the calculation of r4 as the image size depends
;; on that the labels below actually are the first and last positions
;; in the linker-script.
;;
;; Then the locating of the cramfs image depends on the aforementioned
;; image being located in the flash at 0. This is most often not true,
;; thus the following does not work (normally there is a rescue-block
;; between the physical start of the flash and the flash-image start,
;; and when run with compression, the kernel is actually unpacked to
;; DRAM and we never get here in the first place :))
moveq 0, $r0 ; source
move.d text_start, $r1 ; destination
move.d __vmlinux_end, $r2 ; end destination
move.d $r2, $r4
sub.d $r1, $r4 ; r4=__vmlinux_end in flash, used below
1: move.w [$r0+], $r3
move.w $r3, [$r1+]
cmp.d $r2, $r1
blo 1b
nop
;; We keep the cramfs in the flash.
;; There might be none, but that does not matter because
;; we don't do anything than read some bytes here.
moveq 0, $r0
move.d $r0, [romfs_length] ; default if there is no cramfs
move.d [$r4], $r0 ; cramfs_super.magic
cmp.d CRAMFS_MAGIC, $r0
bne 1f
nop
move.d [$r4 + 4], $r0 ; cramfs_super.size
move.d $r0, [romfs_length]
#ifdef CONFIG_CRIS_LOW_MAP
add.d 0x50000000, $r4 ; add flash start in virtual memory (cached)
#else
add.d 0xf0000000, $r4 ; add flash start in virtual memory (cached)
#endif
move.d $r4, [romfs_start]
1:
moveq 1, $r0
move.d $r0, [romfs_in_flash]
jump _start_it ; enter code, cached this time
_inram:
;; Move the ROM fs to after BSS end. This assumes that the cramfs
;; second longword contains the length of the cramfs
moveq 0, $r0
move.d $r0, [romfs_length] ; default if there is no cramfs
;; The kernel could have been unpacked to DRAM by the loader, but
;; the cramfs image could still be in the Flash directly after the
;; compressed kernel image. The loader passes the address of the
;; byte succeeding the last compressed byte in the flash in the
;; register r9 when starting the kernel. Check if r9 points to a
;; decent cramfs image!
;; (Notice that if this is not booted from the loader, r9 will be
;; garbage but we do sanity checks on it, the chance that it points
;; to a cramfs magic is small.. )
cmp.d 0x0ffffff8, $r9
bhs _no_romfs_in_flash ; r9 points outside the flash area
nop
move.d [$r9], $r0 ; cramfs_super.magic
cmp.d CRAMFS_MAGIC, $r0
bne _no_romfs_in_flash
nop
move.d [$r9+4], $r0 ; cramfs_super.length
move.d $r0, [romfs_length]
#ifdef CONFIG_CRIS_LOW_MAP
add.d 0x50000000, $r9 ; add flash start in virtual memory (cached)
#else
add.d 0xf0000000, $r9 ; add flash start in virtual memory (cached)
#endif
move.d $r9, [romfs_start]
moveq 1, $r0
move.d $r0, [romfs_in_flash]
jump _start_it ; enter code, cached this time
_no_romfs_in_flash:
;; Check if there is a cramfs (magic value).
;; Notice that we check for cramfs magic value - which is
;; the "rom fs" we'll possibly use in 2.4 if not JFFS (which does
;; not need this mechanism anyway)
move.d __init_end, $r0; the image will be after the end of init
move.d [$r0], $r1 ; cramfs assumes same endian on host/target
cmp.d CRAMFS_MAGIC, $r1; magic value in cramfs superblock
bne 2f
nop
;; Ok. What is its size ?
move.d [$r0 + 4], $r2 ; cramfs_super.size (again, no need to swapwb)
;; We want to copy it to the end of the BSS
move.d _end, $r1
;; Remember values so cramfs and setup can find this info
move.d $r1, [romfs_start] ; new romfs location
move.d $r2, [romfs_length]
;; We need to copy it backwards, since they can be overlapping
add.d $r2, $r0
add.d $r2, $r1
;; Go ahead. Make my loop.
lsrq 1, $r2 ; size is in bytes, we copy words
1: move.w [$r0=$r0-2],$r3
move.w $r3,[$r1=$r1-2]
subq 1, $r2
bne 1b
nop
2:
;; Dont worry that the BSS is tainted. It will be cleared later.
moveq 0, $r0
move.d $r0, [romfs_in_flash]
jump _start_it ; better skip the additional cramfs check below
_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)
move.d init_thread_union + 8192, $sp
move.d ibr_start,$r0 ; this symbol is set by the linker script
move $r0,$ibr
move.d $r0,[etrax_irv] ; set the interrupt base register and pointer
;; Clear BSS region, from _bss_start to _end
move.d __bss_start, $r0
move.d _end, $r1
1: clear.d [$r0+]
cmp.d $r1, $r0
blo 1b
nop
;; Etrax product HW genconfig setup
moveq 0,$r0
;; Select or disable serial port 2
#ifdef CONFIG_ETRAX_SERIAL_PORT2
or.d IO_STATE (R_GEN_CONFIG, ser2, select),$r0
#else
or.d IO_STATE (R_GEN_CONFIG, ser2, disable),$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, 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
move.d $r0,[genconfig_shadow] ; init a shadow register of R_GEN_CONFIG
move.d $r0,[R_GEN_CONFIG]
#if 0
moveq 4,$r0
move.b $r0,[R_DMA_CH6_CMD] ; reset (ser0 dma out)
move.b $r0,[R_DMA_CH7_CMD] ; reset (ser0 dma in)
1: move.b [R_DMA_CH6_CMD],$r0 ; wait for reset cycle to finish
and.b 7,$r0
cmp.b 4,$r0
beq 1b
nop
1: move.b [R_DMA_CH7_CMD],$r0 ; wait for reset cycle to finish
and.b 7,$r0
cmp.b 4,$r0
beq 1b
nop
#endif
moveq IO_STATE (R_DMA_CH8_CMD, cmd, reset),$r0
move.b $r0,[R_DMA_CH8_CMD] ; reset (ser1 dma out)
move.b $r0,[R_DMA_CH9_CMD] ; reset (ser1 dma in)
1: move.b [R_DMA_CH8_CMD],$r0 ; wait for reset cycle to finish
andq IO_MASK (R_DMA_CH8_CMD, cmd),$r0
cmpq IO_STATE (R_DMA_CH8_CMD, cmd, reset),$r0
beq 1b
nop
1: move.b [R_DMA_CH9_CMD],$r0 ; wait for reset cycle to finish
andq IO_MASK (R_DMA_CH9_CMD, cmd),$r0
cmpq IO_STATE (R_DMA_CH9_CMD, cmd, reset),$r0
beq 1b
nop
;; setup port PA and PB default initial directions and data
;; including their shadow registers
move.b CONFIG_ETRAX_DEF_R_PORT_PA_DIR,$r0
move.b $r0,[port_pa_dir_shadow]
move.b $r0,[R_PORT_PA_DIR]
move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA,$r0
move.b $r0,[port_pa_data_shadow]
move.b $r0,[R_PORT_PA_DATA]
move.b CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG,$r0
move.b $r0,[port_pb_config_shadow]
move.b $r0,[R_PORT_PB_CONFIG]
move.b CONFIG_ETRAX_DEF_R_PORT_PB_DIR,$r0
move.b $r0,[port_pb_dir_shadow]
move.b $r0,[R_PORT_PB_DIR]
move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA,$r0
move.b $r0,[port_pb_data_shadow]
move.b $r0,[R_PORT_PB_DATA]
moveq 0, $r0
move.d $r0,[port_pb_i2c_shadow]
move.d $r0, [R_PORT_PB_I2C]
moveq 0,$r0
move.d $r0,[port_g_data_shadow]
move.d $r0,[R_PORT_G_DATA]
;; setup the serial port 0 at 115200 baud for debug purposes
moveq IO_STATE (R_SERIAL0_XOFF, tx_stop, enable) \
| IO_STATE (R_SERIAL0_XOFF, auto_xoff, disable) \
| IO_FIELD (R_SERIAL0_XOFF, xoff_char, 0),$r0
move.d $r0,[R_SERIAL0_XOFF]
; 115.2kbaud for both transmit and receive
move.b IO_STATE (R_SERIAL0_BAUD, tr_baud, c115k2Hz) \
| IO_STATE (R_SERIAL0_BAUD, rec_baud, c115k2Hz),$r0
move.b $r0,[R_SERIAL0_BAUD]
; Set up and enable the serial0 receiver.
move.b 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) \
| IO_STATE (R_SERIAL0_REC_CTRL, sampling, middle) \
| IO_STATE (R_SERIAL0_REC_CTRL, rec_stick_par, normal) \
| IO_STATE (R_SERIAL0_REC_CTRL, rec_par, even) \
| IO_STATE (R_SERIAL0_REC_CTRL, rec_par_en, disable) \
| IO_STATE (R_SERIAL0_REC_CTRL, rec_bitnr, rec_8bit),$r0
move.b $r0,[R_SERIAL0_REC_CTRL]
; Set up and enable the serial0 transmitter.
move.b 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) \
| IO_STATE (R_SERIAL0_TR_CTRL, stop_bits, one_bit) \
| IO_STATE (R_SERIAL0_TR_CTRL, tr_stick_par, normal) \
| IO_STATE (R_SERIAL0_TR_CTRL, tr_par, even) \
| IO_STATE (R_SERIAL0_TR_CTRL, tr_par_en, disable) \
| IO_STATE (R_SERIAL0_TR_CTRL, tr_bitnr, tr_8bit),$r0
move.b $r0,[R_SERIAL0_TR_CTRL]
;; setup the serial port 1 at 115200 baud for debug purposes
moveq IO_STATE (R_SERIAL1_XOFF, tx_stop, enable) \
| IO_STATE (R_SERIAL1_XOFF, auto_xoff, disable) \
| IO_FIELD (R_SERIAL1_XOFF, xoff_char, 0),$r0
move.d $r0,[R_SERIAL1_XOFF]
; 115.2kbaud for both transmit and receive
move.b IO_STATE (R_SERIAL1_BAUD, tr_baud, c115k2Hz) \
| IO_STATE (R_SERIAL1_BAUD, rec_baud, c115k2Hz),$r0
move.b $r0,[R_SERIAL1_BAUD]
; Set up and enable the serial1 receiver.
move.b IO_STATE (R_SERIAL1_REC_CTRL, dma_err, stop) \
| IO_STATE (R_SERIAL1_REC_CTRL, rec_enable, enable) \
| IO_STATE (R_SERIAL1_REC_CTRL, rts_, active) \
| IO_STATE (R_SERIAL1_REC_CTRL, sampling, middle) \
| IO_STATE (R_SERIAL1_REC_CTRL, rec_stick_par, normal) \
| IO_STATE (R_SERIAL1_REC_CTRL, rec_par, even) \
| IO_STATE (R_SERIAL1_REC_CTRL, rec_par_en, disable) \
| IO_STATE (R_SERIAL1_REC_CTRL, rec_bitnr, rec_8bit),$r0
move.b $r0,[R_SERIAL1_REC_CTRL]
; Set up and enable the serial1 transmitter.
move.b IO_FIELD (R_SERIAL1_TR_CTRL, txd, 0) \
| IO_STATE (R_SERIAL1_TR_CTRL, tr_enable, enable) \
| IO_STATE (R_SERIAL1_TR_CTRL, auto_cts, disabled) \
| IO_STATE (R_SERIAL1_TR_CTRL, stop_bits, one_bit) \
| IO_STATE (R_SERIAL1_TR_CTRL, tr_stick_par, normal) \
| IO_STATE (R_SERIAL1_TR_CTRL, tr_par, even) \
| IO_STATE (R_SERIAL1_TR_CTRL, tr_par_en, disable) \
| IO_STATE (R_SERIAL1_TR_CTRL, tr_bitnr, tr_8bit),$r0
move.b $r0,[R_SERIAL1_TR_CTRL]
#ifdef CONFIG_ETRAX_SERIAL_PORT2
;; setup the serial port 2 at 115200 baud for debug purposes
moveq IO_STATE (R_SERIAL2_XOFF, tx_stop, enable) \
| IO_STATE (R_SERIAL2_XOFF, auto_xoff, disable) \
| IO_FIELD (R_SERIAL2_XOFF, xoff_char, 0),$r0
move.d $r0,[R_SERIAL2_XOFF]
; 115.2kbaud for both transmit and receive
move.b IO_STATE (R_SERIAL2_BAUD, tr_baud, c115k2Hz) \
| IO_STATE (R_SERIAL2_BAUD, rec_baud, c115k2Hz),$r0
move.b $r0,[R_SERIAL2_BAUD]
; Set up and enable the serial2 receiver.
move.b IO_STATE (R_SERIAL2_REC_CTRL, dma_err, stop) \
| IO_STATE (R_SERIAL2_REC_CTRL, rec_enable, enable) \
| IO_STATE (R_SERIAL2_REC_CTRL, rts_, active) \
| IO_STATE (R_SERIAL2_REC_CTRL, sampling, middle) \
| IO_STATE (R_SERIAL2_REC_CTRL, rec_stick_par, normal) \
| IO_STATE (R_SERIAL2_REC_CTRL, rec_par, even) \
| IO_STATE (R_SERIAL2_REC_CTRL, rec_par_en, disable) \
| IO_STATE (R_SERIAL2_REC_CTRL, rec_bitnr, rec_8bit),$r0
move.b $r0,[R_SERIAL2_REC_CTRL]
; Set up and enable the serial2 transmitter.
move.b IO_FIELD (R_SERIAL2_TR_CTRL, txd, 0) \
| IO_STATE (R_SERIAL2_TR_CTRL, tr_enable, enable) \
| IO_STATE (R_SERIAL2_TR_CTRL, auto_cts, disabled) \
| IO_STATE (R_SERIAL2_TR_CTRL, stop_bits, one_bit) \
| IO_STATE (R_SERIAL2_TR_CTRL, tr_stick_par, normal) \
| IO_STATE (R_SERIAL2_TR_CTRL, tr_par, even) \
| IO_STATE (R_SERIAL2_TR_CTRL, tr_par_en, disable) \
| IO_STATE (R_SERIAL2_TR_CTRL, tr_bitnr, tr_8bit),$r0
move.b $r0,[R_SERIAL2_TR_CTRL]
#endif
#ifdef CONFIG_ETRAX_SERIAL_PORT3
;; setup the serial port 3 at 115200 baud for debug purposes
moveq IO_STATE (R_SERIAL3_XOFF, tx_stop, enable) \
| IO_STATE (R_SERIAL3_XOFF, auto_xoff, disable) \
| IO_FIELD (R_SERIAL3_XOFF, xoff_char, 0),$r0
move.d $r0,[R_SERIAL3_XOFF]
; 115.2kbaud for both transmit and receive
move.b IO_STATE (R_SERIAL3_BAUD, tr_baud, c115k2Hz) \
| IO_STATE (R_SERIAL3_BAUD, rec_baud, c115k2Hz),$r0
move.b $r0,[R_SERIAL3_BAUD]
; Set up and enable the serial3 receiver.
move.b IO_STATE (R_SERIAL3_REC_CTRL, dma_err, stop) \
| IO_STATE (R_SERIAL3_REC_CTRL, rec_enable, enable) \
| IO_STATE (R_SERIAL3_REC_CTRL, rts_, active) \
| IO_STATE (R_SERIAL3_REC_CTRL, sampling, middle) \
| IO_STATE (R_SERIAL3_REC_CTRL, rec_stick_par, normal) \
| IO_STATE (R_SERIAL3_REC_CTRL, rec_par, even) \
| IO_STATE (R_SERIAL3_REC_CTRL, rec_par_en, disable) \
| IO_STATE (R_SERIAL3_REC_CTRL, rec_bitnr, rec_8bit),$r0
move.b $r0,[R_SERIAL3_REC_CTRL]
; Set up and enable the serial3 transmitter.
move.b IO_FIELD (R_SERIAL3_TR_CTRL, txd, 0) \
| IO_STATE (R_SERIAL3_TR_CTRL, tr_enable, enable) \
| IO_STATE (R_SERIAL3_TR_CTRL, auto_cts, disabled) \
| IO_STATE (R_SERIAL3_TR_CTRL, stop_bits, one_bit) \
| IO_STATE (R_SERIAL3_TR_CTRL, tr_stick_par, normal) \
| IO_STATE (R_SERIAL3_TR_CTRL, tr_par, even) \
| IO_STATE (R_SERIAL3_TR_CTRL, tr_par_en, disable) \
| IO_STATE (R_SERIAL3_TR_CTRL, tr_bitnr, tr_8bit),$r0
move.b $r0,[R_SERIAL3_TR_CTRL]
#endif
jump start_kernel ; jump into the C-function start_kernel in init/main.c
.data
etrax_irv:
.dword 0
romfs_start:
.dword 0
romfs_length:
.dword 0
romfs_in_flash:
.dword 0
;; put some special pages at the beginning of the kernel aligned
;; to page boundaries - the kernel cannot start until after this
#ifdef CONFIG_CRIS_LOW_MAP
swapper_pg_dir = 0x60002000
#else
swapper_pg_dir = 0xc0002000
#endif
.section ".init.data", "aw"
#include "../lib/hw_settings.S"

File diff suppressed because it is too large Load Diff

View File

@ -1,236 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/cris/kernel/irq.c
*
* Copyright (c) 2000-2002 Axis Communications AB
*
* Authors: Bjorn Wesen (bjornw@axis.com)
*
* This file contains the interrupt vectors and some
* helper functions
*
*/
#include <asm/irq.h>
#include <asm/current.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/init.h>
#define crisv10_mask_irq(irq_nr) (*R_VECT_MASK_CLR = 1 << (irq_nr));
#define crisv10_unmask_irq(irq_nr) (*R_VECT_MASK_SET = 1 << (irq_nr));
extern void kgdb_init(void);
extern void breakpoint(void);
/* don't use set_int_vector, it bypasses the linux interrupt handlers. it is
* global just so that the kernel gdb can use it.
*/
void
set_int_vector(int n, irqvectptr addr)
{
etrax_irv->v[n + 0x20] = (irqvectptr)addr;
}
/* the breakpoint vector is obviously not made just like the normal irq handlers
* but needs to contain _code_ to jump to addr.
*
* the BREAK n instruction jumps to IBR + n * 8
*/
void
set_break_vector(int n, irqvectptr addr)
{
unsigned short *jinstr = (unsigned short *)&etrax_irv->v[n*2];
unsigned long *jaddr = (unsigned long *)(jinstr + 1);
/* if you don't know what this does, do not touch it! */
*jinstr = 0x0d3f;
*jaddr = (unsigned long)addr;
/* 00000026 <clrlop+1a> 3f0d82000000 jump 0x82 */
}
/*
* This builds up the IRQ handler stubs using some ugly macros in irq.h
*
* These macros create the low-level assembly IRQ routines that do all
* the operations that are needed. They are also written to be fast - and to
* disable interrupts as little as humanly possible.
*
*/
/* IRQ0 and 1 are special traps */
void hwbreakpoint(void);
void IRQ1_interrupt(void);
BUILD_TIMER_IRQ(2, 0x04) /* the timer interrupt is somewhat special */
BUILD_IRQ(3, 0x08)
BUILD_IRQ(4, 0x10)
BUILD_IRQ(5, 0x20)
BUILD_IRQ(6, 0x40)
BUILD_IRQ(7, 0x80)
BUILD_IRQ(8, 0x100)
BUILD_IRQ(9, 0x200)
BUILD_IRQ(10, 0x400)
BUILD_IRQ(11, 0x800)
BUILD_IRQ(12, 0x1000)
BUILD_IRQ(13, 0x2000)
void mmu_bus_fault(void); /* IRQ 14 is the bus fault interrupt */
void multiple_interrupt(void); /* IRQ 15 is the multiple IRQ interrupt */
BUILD_IRQ(16, 0x10000 | 0x20000) /* ethernet tx interrupt needs to block rx */
BUILD_IRQ(17, 0x20000 | 0x10000) /* ...and vice versa */
BUILD_IRQ(18, 0x40000)
BUILD_IRQ(19, 0x80000)
BUILD_IRQ(20, 0x100000)
BUILD_IRQ(21, 0x200000)
BUILD_IRQ(22, 0x400000)
BUILD_IRQ(23, 0x800000)
BUILD_IRQ(24, 0x1000000)
BUILD_IRQ(25, 0x2000000)
/* IRQ 26-30 are reserved */
BUILD_IRQ(31, 0x80000000)
/*
* Pointers to the low-level handlers
*/
static void (*interrupt[NR_IRQS])(void) = {
NULL, NULL, IRQ2_interrupt, IRQ3_interrupt,
IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt,
IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt,
IRQ12_interrupt, IRQ13_interrupt, NULL, NULL,
IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt,
IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt,
IRQ24_interrupt, IRQ25_interrupt, NULL, NULL, NULL, NULL, NULL,
IRQ31_interrupt
};
static void enable_crisv10_irq(struct irq_data *data)
{
crisv10_unmask_irq(data->irq);
}
static void disable_crisv10_irq(struct irq_data *data)
{
crisv10_mask_irq(data->irq);
}
static struct irq_chip crisv10_irq_type = {
.name = "CRISv10",
.irq_shutdown = disable_crisv10_irq,
.irq_enable = enable_crisv10_irq,
.irq_disable = disable_crisv10_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 */
extern void do_IRQ(int irq, struct pt_regs * regs);
/* Handle multiple IRQs */
void do_multiple_IRQ(struct pt_regs* regs)
{
int bit;
unsigned masked;
unsigned mask;
unsigned ethmask = 0;
/* Get interrupts to mask and handle */
mask = masked = *R_VECT_MASK_RD;
/* Never mask timer IRQ */
mask &= ~(IO_MASK(R_VECT_MASK_RD, timer0));
/*
* If either ethernet interrupt (rx or tx) is active then block
* the other one too. Unblock afterwards also.
*/
if (mask &
(IO_STATE(R_VECT_MASK_RD, dma0, active) |
IO_STATE(R_VECT_MASK_RD, dma1, active))) {
ethmask = (IO_MASK(R_VECT_MASK_RD, dma0) |
IO_MASK(R_VECT_MASK_RD, dma1));
}
/* Block them */
*R_VECT_MASK_CLR = (mask | ethmask);
/* An extra irq_enter here to prevent softIRQs to run after
* each do_IRQ. This will decrease the interrupt latency.
*/
irq_enter();
/* Handle all IRQs */
for (bit = 2; bit < 32; bit++) {
if (masked & (1 << bit)) {
do_IRQ(bit, regs);
}
}
/* This irq_exit() will trigger the soft IRQs. */
irq_exit();
/* Unblock the IRQs again */
*R_VECT_MASK_SET = (masked | ethmask);
}
/* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and
setting the irq vector table.
*/
void __init init_IRQ(void)
{
int i;
/* clear all interrupt masks */
*R_IRQ_MASK0_CLR = 0xffffffff;
*R_IRQ_MASK1_CLR = 0xffffffff;
*R_IRQ_MASK2_CLR = 0xffffffff;
*R_VECT_MASK_CLR = 0xffffffff;
for (i = 0; i < 256; i++)
etrax_irv->v[i] = weird_irq;
/* Initialize IRQ handler descriptors. */
for(i = 2; i < NR_IRQS; i++) {
irq_set_chip_and_handler(i, &crisv10_irq_type,
handle_simple_irq);
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
for all breakpoints */
for (i = 0; i < 16; i++)
set_break_vector(i, do_sigtrap);
/* except IRQ 15 which is the multiple-IRQ handler on Etrax100 */
set_int_vector(15, multiple_interrupt);
/* 0 and 1 which are special breakpoint/NMI traps */
set_int_vector(0, hwbreakpoint);
set_int_vector(1, IRQ1_interrupt);
/* and irq 14 which is the mmu bus fault handler */
set_int_vector(14, mmu_bus_fault);
/* setup the system-call trap, which is reached by BREAK 13 */
set_break_vector(13, system_call);
/* setup a breakpoint handler for debugging used for both user and
kernel mode debugging (which is why it is not inside an ifdef
CONFIG_ETRAX_KGDB) */
set_break_vector(8, gdb_handle_breakpoint);
#ifdef CONFIG_ETRAX_KGDB
/* setup kgdb if its enabled, and break into the debugger */
kgdb_init();
breakpoint();
#endif
}

File diff suppressed because it is too large Load Diff

View File

@ -1,180 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/cris/kernel/process.c
*
* Copyright (C) 1995 Linus Torvalds
* Copyright (C) 2000-2002 Axis Communications AB
*
* Authors: Bjorn Wesen (bjornw@axis.com)
* Mikael Starvik (starvik@axis.com)
*
* This file handles the architecture-dependent parts of process handling..
*/
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <arch/svinto.h>
#include <linux/init.h>
#include <arch/system.h>
#include <linux/ptrace.h>
#ifdef CONFIG_ETRAX_GPIO
void etrax_gpio_wake_up_check(void); /* drivers/gpio.c */
#endif
/*
* We use this if we don't have any better
* idle routine..
*/
void default_idle(void)
{
#ifdef CONFIG_ETRAX_GPIO
etrax_gpio_wake_up_check();
#endif
local_irq_enable();
}
/* if the watchdog is enabled, we can simply disable interrupts and go
* into an eternal loop, and the watchdog will reset the CPU after 0.1s
* if on the other hand the watchdog wasn't enabled, we just enable it and wait
*/
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
/* Since we dont plan to keep on resetting the watchdog,
the key can be arbitrary hence three */
*R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, 3) |
IO_STATE(R_WATCHDOG, enable, start);
#endif
while(1) /* waiting for RETRIBUTION! */ ;
}
/* setup the child's kernel stack with a pt_regs and switch_stack on it.
* it will be un-nested during _resume and _ret_from_sys_call when the
* new thread is scheduled.
*
* also setup the thread switching structure which is used to keep
* thread-specific data during _resumes.
*
*/
asmlinkage void ret_from_fork(void);
asmlinkage void ret_from_kernel_thread(void);
int copy_thread(unsigned long clone_flags, unsigned long usp,
unsigned long arg, struct task_struct *p)
{
struct pt_regs *childregs = task_pt_regs(p);
struct switch_stack *swstack = ((struct switch_stack *)childregs) - 1;
/* put the pt_regs structure at the end of the new kernel stack page and fix it up
* remember that the task_struct doubles as the kernel stack for the task
*/
if (unlikely(p->flags & PF_KTHREAD)) {
memset(swstack, 0,
sizeof(struct switch_stack) + sizeof(struct pt_regs));
swstack->r1 = usp;
swstack->r2 = arg;
childregs->dccr = 1 << I_DCCR_BITNR;
swstack->return_ip = (unsigned long) ret_from_kernel_thread;
p->thread.ksp = (unsigned long) swstack;
p->thread.usp = 0;
return 0;
}
*childregs = *current_pt_regs(); /* struct copy of pt_regs */
childregs->r10 = 0; /* child returns 0 after a fork/clone */
/* put the switch stack right below the pt_regs */
swstack->r9 = 0; /* parameter to ret_from_sys_call, 0 == dont restart the syscall */
/* we want to return into ret_from_sys_call after the _resume */
swstack->return_ip = (unsigned long) ret_from_fork; /* Will call ret_from_sys_call */
/* fix the user-mode stackpointer */
p->thread.usp = usp ?: rdusp();
/* and the kernel-mode one */
p->thread.ksp = (unsigned long) swstack;
#ifdef DEBUG
printk("copy_thread: new regs at 0x%p, as shown below:\n", childregs);
show_registers(childregs);
#endif
return 0;
}
unsigned long get_wchan(struct task_struct *p)
{
#if 0
/* YURGH. TODO. */
unsigned long ebp, esp, eip;
unsigned long stack_page;
int count = 0;
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
stack_page = (unsigned long)p;
esp = p->thread.esp;
if (!stack_page || esp < stack_page || esp > 8188+stack_page)
return 0;
/* include/asm-i386/system.h:switch_to() pushes ebp last. */
ebp = *(unsigned long *) esp;
do {
if (ebp < stack_page || ebp > 8184+stack_page)
return 0;
eip = *(unsigned long *) (ebp+4);
if (!in_sched_functions(eip))
return eip;
ebp = *(unsigned long *) ebp;
} while (count++ < 16);
#endif
return 0;
}
#undef last_sched
#undef first_sched
void show_regs(struct pt_regs * regs)
{
unsigned long usp = rdusp();
show_regs_print_info(KERN_DEFAULT);
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",
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

@ -1,204 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000-2003, Axis Communications AB.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/signal.h>
#include <linux/security.h>
#include <linux/uaccess.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
/*
* Determines which bits in DCCR the user has access to.
* 1 = access, 0 = no access.
*/
#define DCCR_MASK 0x0000001f /* XNZVC */
/*
* Get contents of register REGNO in task TASK.
*/
inline long get_reg(struct task_struct *task, unsigned int regno)
{
/* USP is a special case, it's not in the pt_regs struct but
* in the tasks thread struct
*/
if (regno == PT_USP)
return task->thread.usp;
else if (regno < PT_MAX)
return ((unsigned long *)task_pt_regs(task))[regno];
else
return 0;
}
/*
* Write contents of register REGNO in task TASK.
*/
inline int put_reg(struct task_struct *task, unsigned int regno,
unsigned long data)
{
if (regno == PT_USP)
task->thread.usp = data;
else if (regno < PT_MAX)
((unsigned long *)task_pt_regs(task))[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)
{
/* Todo - pending singlesteps? */
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
}
/*
* Note that this implementation of ptrace behaves differently from vanilla
* ptrace. Contrary to what the man page says, in the PTRACE_PEEKTEXT,
* PTRACE_PEEKDATA, and PTRACE_PEEKUSER requests the data variable is not
* ignored. Instead, the data variable is expected to point at a location
* (in user space) where the result of the ptrace call is written (instead of
* being returned).
*/
long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
int ret;
unsigned int regno = addr >> 2;
unsigned long __user *datap = (unsigned long __user *)data;
switch (request) {
/* Read word at location address. */
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA:
ret = generic_ptrace_peekdata(child, addr, data);
break;
/* Read the word at location address in the USER area. */
case PTRACE_PEEKUSR: {
unsigned long tmp;
ret = -EIO;
if ((addr & 3) || regno > PT_MAX)
break;
tmp = get_reg(child, regno);
ret = put_user(tmp, datap);
break;
}
/* Write the word at location address. */
case PTRACE_POKETEXT:
case PTRACE_POKEDATA:
ret = generic_ptrace_pokedata(child, addr, data);
break;
/* Write the word at location address in the USER area. */
case PTRACE_POKEUSR:
ret = -EIO;
if ((addr & 3) || regno > PT_MAX)
break;
if (regno == PT_DCCR) {
/* don't allow the tracing process to change stuff like
* interrupt enable, kernel/user bit, dma enables etc.
*/
data &= DCCR_MASK;
data |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
}
if (put_reg(child, regno, data))
break;
ret = 0;
break;
/* Get all GP registers from the child. */
case PTRACE_GETREGS: {
int i;
unsigned long tmp;
ret = 0;
for (i = 0; i <= PT_MAX; i++) {
tmp = get_reg(child, i);
if (put_user(tmp, datap)) {
ret = -EFAULT;
break;
}
datap++;
}
break;
}
/* Set all GP registers in the child. */
case PTRACE_SETREGS: {
int i;
unsigned long tmp;
ret = 0;
for (i = 0; i <= PT_MAX; i++) {
if (get_user(tmp, datap)) {
ret = -EFAULT;
break;
}
if (i == PT_DCCR) {
tmp &= DCCR_MASK;
tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
}
put_reg(child, i, tmp);
datap++;
}
break;
}
default:
ret = ptrace_request(child, request, addr, data);
break;
}
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;
}
}

View File

@ -1,107 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
*
* linux/arch/cris/arch-v10/kernel/setup.c
*
* Copyright (C) 1995 Linus Torvalds
* Copyright (c) 2001-2002 Axis Communications AB
*/
/*
* This file handles the architecture-dependent parts of initialization
*/
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/param.h>
#include <arch/system.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
static struct cpu_info {
char *model;
unsigned short cache;
unsigned short flags;
} cpu_info[] = {
/* The first four models will never ever run this code and are
only here for display. */
{ "ETRAX 1", 0, 0 },
{ "ETRAX 2", 0, 0 },
{ "ETRAX 3", 0, HAS_TOKENRING },
{ "ETRAX 4", 0, HAS_TOKENRING | HAS_SCSI },
{ "Unknown", 0, 0 },
{ "Unknown", 0, 0 },
{ "Unknown", 0, 0 },
{ "Simulator", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA },
{ "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG },
{ "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA },
{ "ETRAX 100LX", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU | HAS_MMU_BUG },
{ "ETRAX 100LX v2", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU },
{ "Unknown", 0, 0 } /* This entry MUST be the last */
};
int show_cpuinfo(struct seq_file *m, void *v)
{
unsigned long revision;
struct cpu_info *info;
/* read the version register in the CPU and print some stuff */
revision = rdvr();
if (revision >= ARRAY_SIZE(cpu_info))
info = &cpu_info[ARRAY_SIZE(cpu_info) - 1];
else
info = &cpu_info[revision];
seq_printf(m,
"processor\t: 0\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",
revision,
info->model,
info->cache,
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);
return 0;
}
#endif /* CONFIG_PROC_FS */
void
show_etrax_copyright(void)
{
printk(KERN_INFO
"Linux/CRIS port on ETRAX 100LX (c) 2001 Axis Communications AB\n");
}

View File

@ -1,37 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Various shadow registers. Defines for these are in include/asm-etrax100/io.h
*/
/* 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;
unsigned char port_pb_i2c_shadow;
unsigned char port_pb_config_shadow;
unsigned char port_pb_dir_shadow;
unsigned char port_pb_data_shadow;
unsigned long r_timer_ctrl_shadow;
/* Shadows for external I/O port registers.
* These are only usable if there actually IS a latch connected
* to the corresponding external chip-select pin.
*
* A common usage is that CSP0 controls LEDs and CSP4 video chips.
*/
unsigned long port_cse1_shadow;
unsigned long port_csp0_shadow;
unsigned long port_csp4_shadow;
/* Corresponding addresses for the ports.
* These are initialized in arch/cris/mm/init.c using ioremap.
*/
volatile unsigned long *port_cse1_addr;
volatile unsigned long *port_csp0_addr;
volatile unsigned long *port_csp4_addr;

View File

@ -1,440 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/cris/kernel/signal.c
*
* Based on arch/i386/kernel/signal.c by
* Copyright (C) 1991, 1992 Linus Torvalds
* 1997-11-28 Modified for POSIX.1b signals by Richard Henderson *
*
* Ideas also taken from arch/arm.
*
* Copyright (C) 2000-2007 Axis Communications AB
*
* Authors: Bjorn Wesen (bjornw@axis.com)
*
*/
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/stddef.h>
#include <asm/processor.h>
#include <asm/ucontext.h>
#include <linux/uaccess.h>
#include <arch/system.h>
#define DEBUG_SIG 0
/* a syscall in Linux/CRIS is a break 13 instruction which is 2 bytes */
/* manipulate regs so that upon return, it will be re-executed */
/* We rely on that pc points to the instruction after "break 13", so the
* library must never do strange things like putting it in a delay slot.
*/
#define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->irp -= 2;
void do_signal(int canrestart, struct pt_regs *regs);
/*
* Do a signal return; undo the signal stack.
*/
struct sigframe {
struct sigcontext sc;
unsigned long extramask[_NSIG_WORDS-1];
unsigned char retcode[8]; /* trampoline code */
};
struct rt_sigframe {
struct siginfo *pinfo;
void *puc;
struct siginfo info;
struct ucontext uc;
unsigned char retcode[8]; /* trampoline code */
};
static int
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
{
unsigned int err = 0;
unsigned long old_usp;
/* Always make any pending restarted system calls return -EINTR */
current->restart_block.fn = do_no_restart_syscall;
/* restore the regs from &sc->regs (same as sc, since regs is first)
* (sc is already checked for VERIFY_READ since the sigframe was
* checked in sys_sigreturn previously)
*/
if (__copy_from_user(regs, sc, sizeof(struct pt_regs)))
goto badframe;
/* make sure the U-flag is set so user-mode cannot fool us */
regs->dccr |= 1 << 8;
/* restore the old USP as it was before we stacked the sc etc.
* (we cannot just pop the sigcontext since we aligned the sp and
* stuff after pushing it)
*/
err |= __get_user(old_usp, &sc->usp);
wrusp(old_usp);
/* TODO: the other ports use regs->orig_XX to disable syscall checks
* after this completes, but we don't use that mechanism. maybe we can
* use it now ?
*/
return err;
badframe:
return 1;
}
asmlinkage int sys_sigreturn(void)
{
struct pt_regs *regs = current_pt_regs();
struct sigframe __user *frame = (struct sigframe *)rdusp();
sigset_t set;
/*
* Since we stacked the signal on a dword boundary,
* then frame should be dword aligned here. If it's
* not, then the user is trying to mess with us.
*/
if (((long)frame) & 3)
goto badframe;
if (!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;
set_current_blocked(&set);
if (restore_sigcontext(regs, &frame->sc))
goto badframe;
/* TODO: SIGTRAP when single-stepping as in arm ? */
return regs->r10;
badframe:
force_sig(SIGSEGV, current);
return 0;
}
asmlinkage int sys_rt_sigreturn(void)
{
struct pt_regs *regs = current_pt_regs();
struct rt_sigframe __user *frame = (struct rt_sigframe *)rdusp();
sigset_t set;
/*
* Since we stacked the signal on a dword boundary,
* then frame should be dword aligned here. If it's
* not, then the user is trying to mess with us.
*/
if (((long)frame) & 3)
goto badframe;
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
goto badframe;
set_current_blocked(&set);
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
goto badframe;
if (restore_altstack(&frame->uc.uc_stack))
goto badframe;
return regs->r10;
badframe:
force_sig(SIGSEGV, current);
return 0;
}
/*
* Set up a signal frame.
*/
static int setup_sigcontext(struct sigcontext __user *sc,
struct pt_regs *regs, unsigned long mask)
{
int err = 0;
unsigned long usp = rdusp();
/* copy the regs. they are first in sc so we can use sc directly */
err |= __copy_to_user(sc, regs, sizeof(struct pt_regs));
/* Set the frametype to CRIS_FRAME_NORMAL for the execution of
the signal handler. The frametype will be restored to its previous
value in restore_sigcontext. */
regs->frametype = CRIS_FRAME_NORMAL;
/* then some other stuff */
err |= __put_user(mask, &sc->oldmask);
err |= __put_user(usp, &sc->usp);
return err;
}
/* Figure out where we want to put the new signal frame
* - usually on the stack. */
static inline void __user *
get_sigframe(struct ksignal *ksig, size_t frame_size)
{
unsigned long sp = sigsp(rdusp(), ksig);
/* make sure the frame is dword-aligned */
sp &= ~3;
return (void __user*)(sp - frame_size);
}
/* grab and setup a signal frame.
*
* basically we stack a lot of state info, and arrange for the
* user-mode program to return to the kernel using either a
* trampoline which performs the syscall sigreturn, or a provided
* user-mode trampoline.
*/
static int setup_frame(struct ksignal *ksig, sigset_t *set,
struct pt_regs *regs)
{
struct sigframe __user *frame;
unsigned long return_ip;
int err = 0;
frame = get_sigframe(ksig, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
return -EFAULT;
err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
if (err)
return -EFAULT;
if (_NSIG_WORDS > 1) {
err |= __copy_to_user(frame->extramask, &set->sig[1],
sizeof(frame->extramask));
}
if (err)
return -EFAULT;
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
return_ip = (unsigned long)ksig->ka.sa.sa_restorer;
} else {
/* trampoline - the desired return ip is the retcode itself */
return_ip = (unsigned long)&frame->retcode;
/* This is movu.w __NR_sigreturn, r9; break 13; */
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)
return -EFAULT;
/* Set up registers for signal handler */
regs->irp = (unsigned long) ksig->ka.sa.sa_handler; /* what we enter NOW */
regs->srp = return_ip; /* what we enter LATER */
regs->r10 = ksig->sig; /* first argument is signo */
/* actually move the usp to reflect the stacked frame */
wrusp((unsigned long)frame);
return 0;
}
static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
unsigned long return_ip;
int err = 0;
frame = get_sigframe(ksig, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
return -EFAULT;
err |= __put_user(&frame->info, &frame->pinfo);
err |= __put_user(&frame->uc, &frame->puc);
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
if (err)
return -EFAULT;
/* 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));
err |= __save_altstack(&frame->uc.uc_stack, rdusp());
if (err)
return -EFAULT;
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
return_ip = (unsigned long)ksig->ka.sa.sa_restorer;
} else {
/* trampoline - the desired return ip is the retcode itself */
return_ip = (unsigned long)&frame->retcode;
/* This is movu.w __NR_rt_sigreturn, r9; break 13; */
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)
return -EFAULT;
/* Set up registers for signal handler */
/* What we enter NOW */
regs->irp = (unsigned long) ksig->ka.sa.sa_handler;
/* What we enter LATER */
regs->srp = return_ip;
/* First argument is signo */
regs->r10 = ksig->sig;
/* Second argument is (siginfo_t *) */
regs->r11 = (unsigned long)&frame->info;
/* Third argument is unused */
regs->r12 = 0;
/* Actually move the usp to reflect the stacked frame */
wrusp((unsigned long)frame);
return 0;
}
/*
* OK, we're invoking a handler
*/
static inline void handle_signal(int canrestart, struct ksignal *ksig,
struct pt_regs *regs)
{
sigset_t *oldset = sigmask_to_save();
int ret;
/* Are we from a system call? */
if (canrestart) {
/* If so, check system call restarting.. */
switch (regs->r10) {
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND:
/* ERESTARTNOHAND means that the syscall should
* only be restarted if there was no handler for
* the signal, and since we only get here if there
* is a handler, we don't restart */
regs->r10 = -EINTR;
break;
case -ERESTARTSYS:
/* ERESTARTSYS means to restart the syscall if
* there is no handler or the handler was
* registered with SA_RESTART */
if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
regs->r10 = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
/* ERESTARTNOINTR means that the syscall should
* be called again after the signal handler returns. */
RESTART_CRIS_SYS(regs);
}
}
/* Set up the stack frame */
if (ksig->ka.sa.sa_flags & SA_SIGINFO)
ret = setup_rt_frame(ksig, oldset, regs);
else
ret = setup_frame(ksig, oldset, regs);
signal_setup_done(ret, ksig, 0);
}
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*
* 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.
*/
void do_signal(int canrestart, struct pt_regs *regs)
{
struct ksignal ksig;
/*
* We want the common case to go fast, which
* is why we may in certain cases get here from
* kernel mode. Just return without doing anything
* if so.
*/
if (!user_mode(regs))
return;
if (get_signal(&ksig)) {
/* Whee! Actually deliver the signal. */
handle_signal(canrestart, &ksig, regs);
return;
}
/* Did we come from a system call? */
if (canrestart) {
/* Restart the system call - no handlers present */
if (regs->r10 == -ERESTARTNOHAND ||
regs->r10 == -ERESTARTSYS ||
regs->r10 == -ERESTARTNOINTR) {
RESTART_CRIS_SYS(regs);
}
if (regs->r10 == -ERESTART_RESTARTBLOCK) {
regs->r9 = __NR_restart_syscall;
regs->irp -= 2;
}
}
/* if there's no signal to deliver, we just put the saved sigmask
* back */
restore_saved_sigmask();
}

View File

@ -1,268 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/cris/arch-v10/kernel/time.c
*
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
* Copyright (C) 1999-2002 Axis Communications AB
*
*/
#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/mm.h>
#include <asm/types.h>
#include <asm/signal.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <asm/irq_regs.h>
/* define this if you need to use print_timestamp */
/* it will make jiffies at 96 hz instead of 100 hz though */
#undef USE_CASCADE_TIMERS
unsigned long get_ns_in_jiffie(void)
{
unsigned char timer_count, t1;
unsigned short presc_count;
unsigned long ns;
unsigned long flags;
local_irq_save(flags);
timer_count = *R_TIMER0_DATA;
presc_count = *R_TIM_PRESC_STATUS;
/* presc_count might be wrapped */
t1 = *R_TIMER0_DATA;
if (timer_count != t1){
/* it wrapped, read prescaler again... */
presc_count = *R_TIM_PRESC_STATUS;
timer_count = t1;
}
local_irq_restore(flags);
if (presc_count >= PRESCALE_VALUE/2 ){
presc_count = PRESCALE_VALUE - presc_count + PRESCALE_VALUE/2;
} else {
presc_count = PRESCALE_VALUE - presc_count - PRESCALE_VALUE/2;
}
ns = ( (TIMER0_DIV - timer_count) * ((1000000000/HZ)/TIMER0_DIV )) +
( (presc_count) * (1000000000/PRESCALE_FREQ));
return ns;
}
static u32 cris_v10_gettimeoffset(void)
{
u32 count;
/* 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 = *R_TIMER0_DATA;
/* Convert timer value to nsec */
return (TIMER0_DIV - count) * (NSEC_PER_SEC/HZ)/TIMER0_DIV;
}
/* Excerpt from the Etrax100 HSDD about the built-in watchdog:
*
* 3.10.4 Watchdog timer
* When the watchdog timer is started, it generates an NMI if the watchdog
* isn't restarted or stopped within 0.1 s. If it still isn't restarted or
* stopped after an additional 3.3 ms, the watchdog resets the chip.
* The watchdog timer is stopped after reset. The watchdog timer is controlled
* by the R_WATCHDOG register. The R_WATCHDOG register contains an enable bit
* and a 3-bit key value. The effect of writing to the R_WATCHDOG register is
* described in the table below:
*
* Watchdog Value written:
* state: To enable: To key: Operation:
* -------- ---------- ------- ----------
* stopped 0 X No effect.
* stopped 1 key_val Start watchdog with key = key_val.
* started 0 ~key Stop watchdog
* started 1 ~key Restart watchdog with key = ~key.
* started X new_key_val Change key to new_key_val.
*
* Note: '~' is the bitwise NOT operator.
*
*/
/* right now, starting the watchdog is the same as resetting it */
#define start_watchdog reset_watchdog
#ifdef CONFIG_ETRAX_WATCHDOG
static int watchdog_key = 0; /* arbitrary 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)
/* only keep watchdog happy as long as we have memory left! */
if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) {
/* reset the watchdog with the inverse of the old key */
watchdog_key ^= 0x7; /* invert key, which is 3 bits */
*R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, watchdog_key) |
IO_STATE(R_WATCHDOG, enable, start);
}
#endif
}
/* stop the watchdog - we still need the correct key */
void stop_watchdog(void)
{
#ifdef CONFIG_ETRAX_WATCHDOG
watchdog_key ^= 0x7; /* invert key, which is 3 bits */
*R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, watchdog_key) |
IO_STATE(R_WATCHDOG, enable, stop);
#endif
}
extern void cris_do_profile(struct pt_regs *regs);
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "xtime_update()" routine every clocktick
*/
static inline irqreturn_t timer_interrupt(int irq, void *dev_id)
{
struct pt_regs *regs = get_irq_regs();
/* acknowledge the timer irq */
#ifdef USE_CASCADE_TIMERS
*R_TIMER_CTRL =
IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) |
IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) |
IO_STATE( R_TIMER_CTRL, i1, clr) |
IO_STATE( R_TIMER_CTRL, tm1, run) |
IO_STATE( R_TIMER_CTRL, clksel1, cascade0) |
IO_STATE( R_TIMER_CTRL, i0, clr) |
IO_STATE( R_TIMER_CTRL, tm0, run) |
IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz);
#else
*R_TIMER_CTRL = r_timer_ctrl_shadow | IO_STATE(R_TIMER_CTRL, i0, clr);
#endif
/* reset watchdog otherwise it resets us! */
reset_watchdog();
/* Update statistics. */
update_process_times(user_mode(regs));
/* call the real timer interrupt handler */
xtime_update(1);
cris_do_profile(regs); /* Save profiling information */
return IRQ_HANDLED;
}
/* timer is IRQF_SHARED so drivers can add stuff to the timer irq chain */
static struct irqaction irq2 = {
.handler = timer_interrupt,
.flags = IRQF_SHARED,
.name = "timer",
};
void __init time_init(void)
{
arch_gettimeoffset = cris_v10_gettimeoffset;
/* 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;
/* Setup the etrax timers
* Base frequency is 25000 hz, divider 250 -> 100 HZ
* In normal mode, we use timer0, so timer1 is free. In cascade
* mode (which we sometimes use for debugging) both timers are used.
* Remember that linux/timex.h contains #defines that rely on the
* timer settings below (hz and divide factor) !!!
*/
#ifdef USE_CASCADE_TIMERS
*R_TIMER_CTRL =
IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) |
IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) |
IO_STATE( R_TIMER_CTRL, i1, nop) |
IO_STATE( R_TIMER_CTRL, tm1, stop_ld) |
IO_STATE( R_TIMER_CTRL, clksel1, cascade0) |
IO_STATE( R_TIMER_CTRL, i0, nop) |
IO_STATE( R_TIMER_CTRL, tm0, stop_ld) |
IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz);
*R_TIMER_CTRL = r_timer_ctrl_shadow =
IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) |
IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) |
IO_STATE( R_TIMER_CTRL, i1, nop) |
IO_STATE( R_TIMER_CTRL, tm1, run) |
IO_STATE( R_TIMER_CTRL, clksel1, cascade0) |
IO_STATE( R_TIMER_CTRL, i0, nop) |
IO_STATE( R_TIMER_CTRL, tm0, run) |
IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz);
#else
*R_TIMER_CTRL =
IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) |
IO_FIELD(R_TIMER_CTRL, timerdiv0, TIMER0_DIV) |
IO_STATE(R_TIMER_CTRL, i1, nop) |
IO_STATE(R_TIMER_CTRL, tm1, stop_ld) |
IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) |
IO_STATE(R_TIMER_CTRL, i0, nop) |
IO_STATE(R_TIMER_CTRL, tm0, stop_ld) |
IO_STATE(R_TIMER_CTRL, clksel0, flexible);
*R_TIMER_CTRL = r_timer_ctrl_shadow =
IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) |
IO_FIELD(R_TIMER_CTRL, timerdiv0, TIMER0_DIV) |
IO_STATE(R_TIMER_CTRL, i1, nop) |
IO_STATE(R_TIMER_CTRL, tm1, run) |
IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) |
IO_STATE(R_TIMER_CTRL, i0, nop) |
IO_STATE(R_TIMER_CTRL, tm0, run) |
IO_STATE(R_TIMER_CTRL, clksel0, flexible);
*R_TIMER_PRESCALE = PRESCALE_VALUE;
#endif
/* unmask the timer irq */
*R_IRQ_MASK0_SET = IO_STATE(R_IRQ_MASK0_SET, timer0, set);
/* now actually register the irq handler that calls timer_interrupt() */
setup_irq(2, &irq2); /* irq 2 is the timer0 irq in etrax */
/* 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. */
asm ("setf m");
*R_IRQ_MASK0_SET = IO_STATE(R_IRQ_MASK0_SET, watchdog_nmi, set);
*R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, nmi, set);
#endif
}

View File

@ -1,134 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Helper functions for trap handlers
*
* Copyright (C) 2000-2007, Axis Communications AB.
*
* Authors: Bjorn Wesen
* Hans-Peter Nilsson
*
*/
#include <linux/ptrace.h>
#include <linux/uaccess.h>
#include <linux/sched/debug.h>
#include <arch/sv_addr_ag.h>
#include <arch/system.h>
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 process for all cases this
* function is called, and current->thread.usp isn't up to date for the
* current process. Experience shows that using USP is the way to go.
*/
unsigned long usp = rdusp();
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",
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 sp: %08lx\n",
regs->r12, regs->r13, regs->orig_r10, (long unsigned)regs);
printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
printk("Process %s (pid: %d, stackpage=%08lx)\n",
current->comm, current->pid, (unsigned long)current);
/*
* When in-kernel, we also print out the stack and code at the
* time of the fault..
*/
if (!user_mode(regs)) {
int i;
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);
printk("\nCode: ");
if (regs->irp < PAGE_OFFSET)
goto bad_value;
/*
* Quite often the value at regs->irp 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->irp
* location is pointed out in a ksymoops-friendly way by
* wrapping the byte for that address in parenthesises.
*/
for (i = -12; i < 12; i++) {
unsigned char c;
if (__get_user(c, &((unsigned char *)regs->irp)[i])) {
bad_value:
printk(" Bad IP value.");
break;
}
if (i == 0)
printk("(%02x) ", c);
else
printk("%02x ", c);
}
printk("\n");
}
}
void
arch_enable_nmi(void)
{
asm volatile ("setf m");
}
extern void (*nmi_handler)(struct pt_regs *);
void handle_nmi(struct pt_regs *regs)
{
if (nmi_handler)
nmi_handler(regs);
/* Wait until nmi is no longer active. (We enable NMI immediately after
returning from this function, and we don't want it happening while
exiting from the NMI interrupt handler.) */
while (*R_IRQ_MASK0_RD & IO_STATE(R_IRQ_MASK0_RD, nmi_pin, active))
;
}
#ifdef CONFIG_DEBUG_BUGVERBOSE
void
handle_BUG(struct pt_regs *regs)
{
struct bug_frame f;
unsigned char c;
unsigned long irp = regs->irp;
if (__copy_from_user(&f, (const void __user *)(irp - 8), sizeof f))
return;
if (f.prefix != BUG_PREFIX || f.magic != BUG_MAGIC)
return;
if (__get_user(c, f.filename))
f.filename = "<bad filename>";
printk("kernel BUG at %s:%d!\n", f.filename, f.line);
}
#endif

View File

@ -1,6 +0,0 @@
#
# Makefile for Etrax-specific library files..
#
lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o

View File

@ -1,119 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* A fast checksum routine using movem
* Copyright (c) 1998-2001 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
movem $r8,[$sp]
;; do a movem checksum
subq 10*4,$r11 ; update length for the first loop
_mloop: movem [$r10+],$r9 ; read 10 longwords
;; perform dword checksumming on the 10 longwords
add.d $r0,$r12
ax
add.d $r1,$r12
ax
add.d $r2,$r12
ax
add.d $r3,$r12
ax
add.d $r4,$r12
ax
add.d $r5,$r12
ax
add.d $r6,$r12
ax
add.d $r7,$r12
ax
add.d $r8,$r12
ax
add.d $r9,$r12
;; fold the carry into the checksum, to avoid having to loop the carry
;; back into the top
ax
addq 0,$r12
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
_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

@ -1,127 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* A fast checksum+copy routine using movem
* Copyright (c) 1998, 2001 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
movem $r8, [$sp]
;; do a movem copy and checksum
subq 10*4, $r12 ; update length for the first loop
_mloop: movem [$r10+],$r9 ; read 10 longwords
1: ;; A failing userspace access will have this as PC.
movem $r9,[$r11+] ; write 10 longwords
;; perform dword checksumming on the 10 longwords
add.d $r0,$r13
ax
add.d $r1,$r13
ax
add.d $r2,$r13
ax
add.d $r3,$r13
ax
add.d $r4,$r13
ax
add.d $r5,$r13
ax
add.d $r6,$r13
ax
add.d $r7,$r13
ax
add.d $r8,$r13
ax
add.d $r9,$r13
;; fold the carry into the checksum, to avoid having to loop the carry
;; back into the top
ax
addq 0,$r13
subq 10*4,$r12
bge _mloop
nop
addq 10*4,$r12 ; compensate for last loop underflowing length
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
_no_fold:
cmpq 2,$r12
blt _no_words
nop
;; copy and checksum the rest of the words
subq 2,$r12
_wloop: move.w [$r10+],$r9
2: ;; A failing userspace access will have this as PC.
addu.w $r9,$r13
subq 2,$r12
bge _wloop
move.w $r9,[$r11+]
addq 2,$r12
_no_words:
;; see if we have one odd byte more
cmpq 1,$r12
beq _do_byte
nop
ret
move.d $r13, $r10
_do_byte:
;; copy and checksum the last byte
move.b [$r10],$r9
3: ;; A failing userspace access will have this as PC.
addu.b $r9,$r13
move.b $r9,[$r11]
ret
move.d $r13, $r10

View File

@ -1,65 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Add-on to transform csum_partial_copy_nocheck in checksumcopy.S into
* csum_partial_copy_from_user by adding exception records.
*
* Copyright (C) 2001 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.
moveq -EFAULT,$r9
move.d $r9,[[$sp]]
;; Clear the rest of the destination area using memset. Preserve the
;; checksum for the readable bytes.
push $srp
push $r13
move.d $r11,$r10
clear.d $r11
jsr memset
pop $r10
jump [$sp+]
.previous
.section __ex_table,"a"
.dword 1b,4b
.dword 2b,5b
.dword 3b,6b
.previous

View File

@ -1,147 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* DRAM/SDRAM initialization - alter with care
* This file is intended to be included from other assembler files
*
* Note: This file may not modify r9 because r9 is used to carry
* information from the decompressor to the kernel
*
* Copyright (C) 2000-2012 Axis Communications AB
*
*/
/* Just to be certain the config file is included, we include it here
* explicitly instead of depending on it being included in the file that
* uses this code.
*/
;; 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.
move.d CONFIG_ETRAX_DEF_R_WAITSTATES, $r0
move.d $r0, [R_WAITSTATES]
move.d CONFIG_ETRAX_DEF_R_BUS_CONFIG, $r0
move.d $r0, [R_BUS_CONFIG]
#ifndef CONFIG_ETRAX_SDRAM
move.d CONFIG_ETRAX_DEF_R_DRAM_CONFIG, $r0
move.d $r0, [R_DRAM_CONFIG]
move.d CONFIG_ETRAX_DEF_R_DRAM_TIMING, $r0
move.d $r0, [R_DRAM_TIMING]
#else
;; Samsung SDRAMs seem to require to be initialized twice to work properly.
moveq 2, $r6
_sdram_init:
; Refer to ETRAX 100LX Designers Reference for a description of SDRAM initialization
; Bank configuration
move.d CONFIG_ETRAX_DEF_R_SDRAM_CONFIG, $r0
move.d $r0, [R_SDRAM_CONFIG]
; 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_DEF_R_SDRAM_TIMING, $r2
and.d 0x00ff0000, $r2
bne _set_timing
lsrq 16, $r2
move.d 0x40, $r2 ; Assume 32 bits and CAS latency = 2
move.d CONFIG_ETRAX_DEF_R_SDRAM_TIMING, $r1
move.d $r1, $r3
and.d 0x03, $r1 ; Get CAS latency
and.d 0x1000, $r3 ; 50 or 100 MHz?
beq _speed_50
nop
_speed_100:
cmp.d 0x00, $r1 ; CAS latency = 2?
beq _bw_check
nop
or.d 0x20, $r2 ; CAS latency = 3
ba _bw_check
nop
_speed_50:
cmp.d 0x01, $r1 ; CAS latency = 2?
beq _bw_check
nop
or.d 0x20, $r2 ; CAS latency = 3
_bw_check:
move.d CONFIG_ETRAX_DEF_R_SDRAM_CONFIG, $r1
and.d 0x800000, $r1 ; DRAM width is bit 23
bne _set_timing
nop
lsrq 1, $r2 ; 16 bits. Shift down value.
; Set timing parameters. Starts master clock
_set_timing:
move.d CONFIG_ETRAX_DEF_R_SDRAM_TIMING, $r1
and.d 0x8000f9ff, $r1 ; Make sure mrs data and command is 0
or.d 0x80000000, $r1 ; Make sure sdram enable bit is set
move.d $r1, $r5
or.d 0x0000c000, $r1 ; ref = disable
lslq 16, $r2 ; mrs data starts at bit 16
or.d $r2, $r1
move.d $r1, [R_SDRAM_TIMING]
; 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 $r4
move.b [$r2+], $r4
lslq 9, $r4 ; Command starts at bit 9
or.d $r1, $r4
move.d $r4, [R_SDRAM_TIMING]
nop ; Wait five nop cycles between each command
nop
nop
nop
nop
cmp.d $r2, $r3
bne 1b
nop
move.d $r5, [R_SDRAM_TIMING]
subq 1, $r6
bne _sdram_init
nop
ba _sdram_commands_end
nop
_sdram_commands_start:
.byte 3 ; Precharge
.byte 0 ; nop
.byte 2 ; refresh
.byte 0 ; nop
.byte 2 ; refresh
.byte 0 ; nop
.byte 2 ; refresh
.byte 0 ; nop
.byte 2 ; refresh
.byte 0 ; nop
.byte 2 ; refresh
.byte 0 ; nop
.byte 2 ; refresh
.byte 0 ; nop
.byte 2 ; refresh
.byte 0 ; nop
.byte 2 ; refresh
.byte 0 ; nop
.byte 1 ; mrs
.byte 0 ; nop
_sdram_commands_end:
#endif

View File

@ -1,61 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* 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)
*/
#define PA_SET_VALUE ((CONFIG_ETRAX_DEF_R_PORT_PA_DIR << 8) | \
(CONFIG_ETRAX_DEF_R_PORT_PA_DATA))
#define PB_SET_VALUE ((CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG << 16) | \
(CONFIG_ETRAX_DEF_R_PORT_PB_DIR << 8) | \
(CONFIG_ETRAX_DEF_R_PORT_PB_DATA))
.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
; SDRAM or EDO DRAM?
#ifdef CONFIG_ETRAX_SDRAM
.dword 1
#else
.dword 0
#endif
; Register values
.dword R_WAITSTATES
.dword CONFIG_ETRAX_DEF_R_WAITSTATES
.dword R_BUS_CONFIG
.dword CONFIG_ETRAX_DEF_R_BUS_CONFIG
#ifdef CONFIG_ETRAX_SDRAM
.dword R_SDRAM_CONFIG
.dword CONFIG_ETRAX_DEF_R_SDRAM_CONFIG
.dword R_SDRAM_TIMING
.dword CONFIG_ETRAX_DEF_R_SDRAM_TIMING
#else
.dword R_DRAM_CONFIG
.dword CONFIG_ETRAX_DEF_R_DRAM_CONFIG
.dword R_DRAM_TIMING
.dword CONFIG_ETRAX_DEF_R_DRAM_TIMING
#endif
.dword R_PORT_PA_SET
.dword PA_SET_VALUE
.dword R_PORT_PB_SET
.dword PB_SET_VALUE
.dword 0 ; No more register values

View File

@ -1,259 +0,0 @@
/* A memset for CRIS.
Copyright (C) 1999-2005 Axis Communications.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Neither the name of Axis Communications nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AXIS COMMUNICATIONS AND ITS CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AXIS
COMMUNICATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
/* FIXME: This file should really only be used for reference, as the
result is somewhat depending on gcc generating what we expect rather
than what we describe. An assembly file should be used instead. */
/* Note the multiple occurrence of the expression "12*4", including the
asm. It is hard to get it into the asm in a good way. Thus better to
expose the problem everywhere: no macro. */
/* Assuming one cycle per dword written or read (ok, not really true; the
world is not ideal), and one cycle 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 MEMSET_BY_BLOCK_THRESHOLD (1 * 48)
/* No name ambiguities in this file. */
__asm__ (".syntax no_register_prefix");
void *memset(void *pdst, int c, unsigned int plen)
{
/* Now we want the parameters in special registers. Make sure the
compiler does something usable with 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. Memsetting about 3..4 bytes or less get
penalized here compared to the generic implementation. */
/* This is fragile performancewise at best. Check with newer GCC
releases, if they compile cascaded "x |= x << 8" to sane code. */
__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) /* Inputs. */
: "0" (lc) /* Outputs. */
: "r13"); /* Trash. */
{
register char *dst __asm__ ("r13") = pdst;
if (((unsigned long) pdst & 3) != 0
/* Oops! n = 0 must be a valid 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;
}
}
/* Decide which setting method to use. */
if (n >= MEMSET_BY_BLOCK_THRESHOLD)
{
/* 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-block sizes
suboptimal. */
__asm__ volatile
("\
;; GCC does promise correct register allocations, but let's \n\
;; make sure it keeps its promises. \n\
.ifnc %0-%1-%4,$r13-$r12-$r11 \n\
.error \"GCC reg alloc bug: %0-%1-%4 != $r13-$r12-$r11\" \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\
"
#ifdef __arch_common_v10_v32
/* Cater to branch offset difference between v32 and v10. We
assume the branch below has an 8-bit offset. */
" setf\n"
#endif
" subq 12*4,r12 \n\
bge 0b \n\
movem r11,[r13+] \n\
\n\
;; Compensate for last loop underflowing n. \n\
addq 12*4,r12 \n\
\n\
;; Restore registers from stack. \n\
movem [sp+],r10"
/* Outputs. */
: "=r" (dst), "=r" (n)
/* Inputs. */
: "0" (dst), "1" (n), "r" (lc));
}
/* An ad-hoc unroll, used for 4*12-1..16 bytes. */
while (n >= 16)
{
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
n -= 16;
}
switch (n)
{
case 0:
break;
case 1:
*dst = (char) lc;
break;
case 2:
*(short *) dst = (short) lc;
break;
case 3:
*(short *) dst = (short) lc; dst += 2;
*dst = (char) lc;
break;
case 4:
*(long *) dst = lc;
break;
case 5:
*(long *) dst = lc; dst += 4;
*dst = (char) lc;
break;
case 6:
*(long *) dst = lc; dst += 4;
*(short *) dst = (short) lc;
break;
case 7:
*(long *) dst = lc; dst += 4;
*(short *) dst = (short) lc; dst += 2;
*dst = (char) lc;
break;
case 8:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc;
break;
case 9:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*dst = (char) lc;
break;
case 10:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(short *) dst = (short) lc;
break;
case 11:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(short *) dst = (short) lc; dst += 2;
*dst = (char) lc;
break;
case 12:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(long *) dst = lc;
break;
case 13:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*dst = (char) lc;
break;
case 14:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(short *) dst = (short) lc;
break;
case 15:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(short *) dst = (short) lc; dst += 2;
*dst = (char) lc;
break;
}
}
return return_dst;
}

View File

@ -1,236 +0,0 @@
/* A memcpy for CRIS.
Copyright (C) 1994-2005 Axis Communications.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Neither the name of Axis Communications nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AXIS COMMUNICATIONS AND ITS CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AXIS
COMMUNICATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
/* FIXME: This file should really only be used for reference, as the
result is somewhat depending on gcc generating what we expect rather
than what we describe. An assembly file should be used instead. */
#include <stddef.h>
/* Break even between movem and move16 is really at 38.7 * 2, but
modulo 44, so up to the next multiple of 44, we use ordinary code. */
#define MEMCPY_BY_BLOCK_THRESHOLD (44 * 2)
/* No name ambiguities in this file. */
__asm__ (".syntax no_register_prefix");
void *
memcpy(void *pdst, const void *psrc, size_t pn)
{
/* 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 unsigned char *dst __asm__ ("r13") = pdst;
register unsigned 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--;
*dst = *src;
src++;
dst++;
}
if ((unsigned long) dst & 2)
{
n -= 2;
*(short *) dst = *(short *) src;
src += 2;
dst += 2;
}
}
/* Decide which copying method to use. */
if (n >= MEMCPY_BY_BLOCK_THRESHOLD)
{
/* 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
("\
;; GCC does promise correct register allocations, but let's \n\
;; make sure it keeps its promises. \n\
.ifnc %0-%1-%2,$r13-$r11-$r12 \n\
.error \"GCC reg alloc bug: %0-%1-%4 != $r13-$r12-$r11\" \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\
"
#ifdef __arch_common_v10_v32
/* Cater to branch offset difference between v32 and v10. We
assume the branch below has an 8-bit offset. */
" setf\n"
#endif
" movem [r11+],r10 \n\
subq 44,r12 \n\
bge 0b \n\
movem r10,[r13+] \n\
\n\
;; Compensate for last loop underflowing n. \n\
addq 44,r12 \n\
\n\
;; Restore registers from stack. \n\
movem [sp+],r10"
/* Outputs. */
: "=r" (dst), "=r" (src), "=r" (n)
/* Inputs. */
: "0" (dst), "1" (src), "2" (n));
}
while (n >= 16)
{
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
n -= 16;
}
switch (n)
{
case 0:
break;
case 1:
*dst = *src;
break;
case 2:
*(short *) dst = *(short *) src;
break;
case 3:
*(short *) dst = *(short *) src; dst += 2; src += 2;
*dst = *src;
break;
case 4:
*(long *) dst = *(long *) src;
break;
case 5:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*dst = *src;
break;
case 6:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(short *) dst = *(short *) src;
break;
case 7:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(short *) dst = *(short *) src; dst += 2; src += 2;
*dst = *src;
break;
case 8:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src;
break;
case 9:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*dst = *src;
break;
case 10:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(short *) dst = *(short *) src;
break;
case 11:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(short *) dst = *(short *) src; dst += 2; src += 2;
*dst = *src;
break;
case 12:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src;
break;
case 13:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*dst = *src;
break;
case 14:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(short *) dst = *(short *) src;
break;
case 15:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(short *) dst = *(short *) src; dst += 2; src += 2;
*dst = *src;
break;
}
return return_dst;
}

View File

@ -1,511 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* User address space access functions.
* The non-inlined parts of asm-cris/uaccess.h are here.
*
* Copyright (C) 2000, Axis Communications AB.
*
* Written by Hans-Peter Nilsson.
* Pieces used from memcpy, originally by Kenny Ranerup long time ago.
*/
#include <linux/uaccess.h>
/* Asm:s have been tweaked (within the domain of correctness) to give
satisfactory results for "gcc version 2.96 20000427 (experimental)".
Check regularly...
Note that the PC saved at a bus-fault is the address *after* the
faulting instruction, which means the branch-target for instructions in
delay-slots for taken branches. Note also that the postincrement in
the instruction is performed regardless of bus-fault; the register is
seen updated in fault handlers.
Oh, and on the code formatting issue, to whomever feels like "fixing
it" to Conformity: I'm too "lazy", but why don't you go ahead and "fix"
string.c too. I just don't think too many people will hack this file
for the code format to be an issue. */
/* Copy to userspace. This is based on the memcpy used for
kernel-to-kernel copying; see "string.c". */
unsigned long __copy_user(void __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 alright, it really would need no temporaries, and no
stack space to save stuff on. */
register char *dst __asm__ ("r13") = pdst;
register const char *src __asm__ ("r11") = psrc;
register int n __asm__ ("r12") = pn;
register int retn __asm__ ("r10") = 0;
/* When src is aligned but not dst, this makes a few extra needless
cycles. I believe it would take as many to check that the
re-alignment was unnecessary. */
if (((unsigned long) dst & 3) != 0
/* Don't align if we wouldn't copy more than a few bytes; so we
don't have to check further for overflows. */
&& n >= 3)
{
if ((unsigned long) dst & 1)
{
__asm_copy_to_user_1 (dst, src, retn);
n--;
}
if ((unsigned long) dst & 2)
{
__asm_copy_to_user_2 (dst, src, retn);
n -= 2;
}
}
/* Decide which copying method to use. */
if (n >= 44*2) /* Break even between movem and
move16 is at 38.7*2, but modulo 44. */
{
/* For large copies we use 'movem'. */
/* It is not optimal to tell the compiler about clobbering any
registers; that will move the saving/restoring of those registers
to the function prologue/epilogue, and make non-movem sizes
suboptimal.
This method is not foolproof; it assumes that the "asm reg"
declarations at the beginning of the function really are used
here (beware: they may be moved to temporary registers).
This way, we do not have to save/move the registers around into
temporaries; we can safely use them straight away.
If you want to check that the allocation was right; then
check the equalities in the first comment. It should say
"r13=r13, r11=r11, r12=r12". */
__asm__ volatile ("\
.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\
\n\
; Since the noted PC of a faulting instruction in a delay-slot of a taken \n\
; branch, is that of the branch target, we actually point at the from-movem \n\
; for this case. There is no ambiguity here; if there was a fault in that \n\
; instruction (meaning a kernel oops), the faulted PC would be the address \n\
; after *that* movem. \n\
\n\
0: \n\
movem [$r11+],$r10 \n\
subq 44,$r12 \n\
bge 0b \n\
movem $r10,[$r13+] \n\
1: \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\
\n\
; To provide a correct count in r10 of bytes that failed to be copied, \n\
; we jump back into the loop if the loop-branch was taken. There is no \n\
; performance penalty for sany use; the program will segfault soon enough.\n\
\n\
3: \n\
move.d [$sp],$r10 \n\
addq 44,$r10 \n\
move.d $r10,[$sp] \n\
jump 0b \n\
4: \n\
movem [$sp+],$r10 \n\
addq 44,$r10 \n\
addq 44,$r12 \n\
jump 2b \n\
\n\
.previous \n\
.section __ex_table,\"a\" \n\
.dword 0b,3b \n\
.dword 1b,4b \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, using dword copying in a loop, or
we copy as much as possible with 'movem' and then the last block (<44
bytes) is copied here. This will work since 'movem' will have
updated SRC, DST and N. */
while (n >= 16)
{
__asm_copy_to_user_16 (dst, src, retn);
n -= 16;
}
/* Having a separate by-four loops cuts down on cache footprint.
FIXME: Test with and without; increasing switch to be 0..15. */
while (n >= 4)
{
__asm_copy_to_user_4 (dst, src, retn);
n -= 4;
}
switch (n)
{
case 0:
break;
case 1:
__asm_copy_to_user_1 (dst, src, retn);
break;
case 2:
__asm_copy_to_user_2 (dst, src, retn);
break;
case 3:
__asm_copy_to_user_3 (dst, src, retn);
break;
}
return retn;
}
EXPORT_SYMBOL(__copy_user);
/* Copy from user to kernel. The return-value is the number of bytes that were
inaccessible. */
unsigned long __copy_user_in(void *pdst, const void __user *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 alright, 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 (retn)
goto exception;
}
if (((unsigned long) src & 2) && n >= 2)
{
__asm_copy_from_user_2 (dst, src, retn);
n -= 2;
if (retn)
goto exception;
}
}
/* Decide which copying method to use. */
if (n >= 44*2) /* Break even between movem and
move16 is at 38.7*2, but modulo 44.
FIXME: We use move4 now. */
{
/* For large copies we use 'movem' */
/* It is not optimal to tell the compiler about clobbering any
registers; that will move the saving/restoring of those registers
to the function prologue/epilogue, and make non-movem sizes
suboptimal.
This method is not foolproof; it assumes that the "asm reg"
declarations at the beginning of the function really are used
here (beware: they may be moved to temporary registers).
This way, we do not have to save/move the registers around into
temporaries; we can safely use them straight away.
If you want to check that the allocation was right; then
check the equalities in the first comment. It should say
"r13=r13, r11=r11, r12=r12" */
__asm__ volatile ("\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\
1: \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 \n\
4: \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\
movem [$sp+],$r10 \n\
addq 44,$r12 ;; Get back count before faulting point. \n\
subq 44,$r11 ;; Get back pointer to faulting movem-line. \n\
jump 4b ;; Fall through, pretending the fault didn't happen.\n\
\n\
.previous \n\
.section __ex_table,\"a\" \n\
.dword 1b,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 exception;
}
/* 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;
exception:
return retn + n;
}
EXPORT_SYMBOL(__copy_user_in);
/* 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 alright, it really would need no temporaries, and no
stack space to save stuff on. */
register char *dst __asm__ ("r13") = pto;
register int n __asm__ ("r12") = pn;
register int retn __asm__ ("r10") = 0;
if (((unsigned long) dst & 3) != 0
/* Don't align if we wouldn't copy more than a few bytes. */
&& n >= 3)
{
if ((unsigned long) dst & 1)
{
__asm_clear_1 (dst, retn);
n--;
}
if ((unsigned long) dst & 2)
{
__asm_clear_2 (dst, retn);
n -= 2;
}
}
/* Decide which copying method to use.
FIXME: This number is from the "ordinary" kernel memset. */
if (n >= (1*48))
{
/* For large clears we use 'movem' */
/* It is not optimal to tell the compiler about clobbering any
call-saved registers; that will move the saving/restoring of
those registers to the function prologue/epilogue, and make
non-movem sizes suboptimal.
This method is not foolproof; it assumes that the "asm reg"
declarations at the beginning of the function really are used
here (beware: they may be moved to temporary registers).
This way, we do not have to save/move the registers around into
temporaries; we can safely use them straight away.
If you want to check that the allocation was right; then
check the equalities in the first comment. It should say
something like "r13=r13, r11=r11, r12=r12". */
__asm__ volatile ("\n\
.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\
bge 0b \n\
movem $r11,[$r13+] \n\
1: \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\
move.d [$sp],$r10 \n\
addq 12*4,$r10 \n\
move.d $r10,[$sp] \n\
clear.d $r10 \n\
jump 0b \n\
\n\
4: \n\
movem [$sp+],$r10 \n\
addq 12*4,$r10 \n\
addq 12*4,$r12 \n\
jump 2b \n\
\n\
.previous \n\
.section __ex_table,\"a\" \n\
.dword 0b,3b \n\
.dword 1b,4b \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;
}
EXPORT_SYMBOL(__do_clear_user);

View File

@ -1,6 +0,0 @@
#
# Makefile for the linux cris-specific parts of the memory manager.
#
obj-y := fault.o init.o tlb.o

View File

@ -1,96 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/cris/mm/fault.c
*
* Low level bus fault handler
*
*
* Copyright (C) 2000-2007 Axis Communications AB
*
* Authors: Bjorn Wesen
*
*/
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <asm/pgtable.h>
#include <arch/svinto.h>
#include <asm/mmu_context.h>
/* debug of low-level TLB reload */
#undef DEBUG
#ifdef DEBUG
#define D(x) x
#else
#define D(x)
#endif
extern const struct exception_table_entry
*search_exception_tables(unsigned long addr);
asmlinkage void do_page_fault(unsigned long address, struct pt_regs *regs,
int protection, int writeaccess);
/* fast TLB-fill fault handler
* this is called from entry.S with interrupts disabled
*/
void
handle_mmu_bus_fault(struct pt_regs *regs)
{
int cause;
int select;
#ifdef DEBUG
int index;
int page_id;
int acc, inv;
#endif
pgd_t* pgd = (pgd_t*)per_cpu(current_pgd, smp_processor_id());
pmd_t *pmd;
pte_t pte;
int miss, we, writeac;
unsigned long address;
unsigned long flags;
cause = *R_MMU_CAUSE;
address = cause & PAGE_MASK; /* get faulting address */
select = *R_TLB_SELECT;
#ifdef DEBUG
page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause);
acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause);
inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause);
index = IO_EXTRACT(R_TLB_SELECT, index, select);
#endif
miss = IO_EXTRACT(R_MMU_CAUSE, miss_excp, cause);
we = IO_EXTRACT(R_MMU_CAUSE, we_excp, cause);
writeac = IO_EXTRACT(R_MMU_CAUSE, wr_rd, cause);
D(printk("bus_fault from IRP 0x%lx: addr 0x%lx, miss %d, inv %d, we %d, acc %d, dx %d pid %d\n",
regs->irp, address, miss, inv, we, acc, index, page_id));
/* leave it to the MM system fault handler */
if (miss)
do_page_fault(address, regs, 0, writeac);
else
do_page_fault(address, regs, 1, we);
/* Reload TLB with new entry to avoid an extra miss exception.
* do_page_fault may have flushed the TLB so we have to restore
* the MMU registers.
*/
local_irq_save(flags);
pmd = (pmd_t *)(pgd + pgd_index(address));
if (pmd_none(*pmd))
goto exit;
pte = *pte_offset_kernel(pmd, address);
if (!pte_present(pte))
goto exit;
*R_TLB_SELECT = select;
*R_TLB_HI = cause;
*R_TLB_LO = pte_val(pte);
exit:
local_irq_restore(flags);
}

View File

@ -1,256 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/cris/arch-v10/mm/init.c
*
*/
#include <linux/mmzone.h>
#include <linux/init.h>
#include <linux/bootmem.h>
#include <linux/mm.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 <arch/svinto.h>
extern void tlb_init(void);
/*
* The kernel is already mapped with a kernel segment at kseg_c so
* we don't need to map it with a page table. However head.S also
* temporarily mapped it at kseg_4 so we should set up the ksegs again,
* clear the TLB and do some other paging setup stuff.
*/
void __init
paging_init(void)
{
int i;
unsigned long zones_size[MAX_NR_ZONES];
printk("Setting up paging and the MMU.\n");
/* clear out the init_mm.pgd that will contain the kernel's mappings */
for(i = 0; i < PTRS_PER_PGD; i++)
swapper_pg_dir[i] = __pgd(0);
/* 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;
/* initialise the TLB (tlb.c) */
tlb_init();
/* see README.mm for details on the KSEG setup */
#ifdef CONFIG_CRIS_LOW_MAP
/* Etrax-100 LX version 1 has a bug so that we cannot map anything
* across the 0x80000000 boundary, so we need to shrink the user-virtual
* area to 0x50000000 instead of 0xb0000000 and map things slightly
* different. The unused areas are marked as paged so that we can catch
* freak kernel accesses there.
*
* The ARTPEC chip is mapped at 0xa so we pass that segment straight
* through. We cannot vremap it because the vmalloc area is below 0x8
* and Juliette needs an uncached area above 0x8.
*
* Same thing with 0xc and 0x9, which is memory-mapped I/O on some boards.
* We map them straight over in LOW_MAP, but use vremap in LX version 2.
*/
#define CACHED_BOOTROM (KSEG_F | 0x08000000UL)
*R_MMU_KSEG = ( IO_STATE(R_MMU_KSEG, seg_f, seg ) | /* bootrom */
IO_STATE(R_MMU_KSEG, seg_e, page ) |
IO_STATE(R_MMU_KSEG, seg_d, page ) |
IO_STATE(R_MMU_KSEG, seg_c, page ) |
IO_STATE(R_MMU_KSEG, seg_b, seg ) | /* kernel reg area */
IO_STATE(R_MMU_KSEG, seg_a, page ) |
IO_STATE(R_MMU_KSEG, seg_9, seg ) | /* LED's on some boards */
IO_STATE(R_MMU_KSEG, seg_8, seg ) | /* CSE0/1, flash and I/O */
IO_STATE(R_MMU_KSEG, seg_7, page ) | /* kernel vmalloc area */
IO_STATE(R_MMU_KSEG, seg_6, seg ) | /* kernel DRAM area */
IO_STATE(R_MMU_KSEG, seg_5, seg ) | /* cached flash */
IO_STATE(R_MMU_KSEG, seg_4, page ) | /* user area */
IO_STATE(R_MMU_KSEG, seg_3, page ) | /* user area */
IO_STATE(R_MMU_KSEG, seg_2, page ) | /* user area */
IO_STATE(R_MMU_KSEG, seg_1, page ) | /* user area */
IO_STATE(R_MMU_KSEG, seg_0, page ) ); /* user area */
*R_MMU_KBASE_HI = ( IO_FIELD(R_MMU_KBASE_HI, base_f, 0x3 ) |
IO_FIELD(R_MMU_KBASE_HI, base_e, 0x0 ) |
IO_FIELD(R_MMU_KBASE_HI, base_d, 0x0 ) |
IO_FIELD(R_MMU_KBASE_HI, base_c, 0x0 ) |
IO_FIELD(R_MMU_KBASE_HI, base_b, 0xb ) |
IO_FIELD(R_MMU_KBASE_HI, base_a, 0x0 ) |
IO_FIELD(R_MMU_KBASE_HI, base_9, 0x9 ) |
IO_FIELD(R_MMU_KBASE_HI, base_8, 0x8 ) );
*R_MMU_KBASE_LO = ( IO_FIELD(R_MMU_KBASE_LO, base_7, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_6, 0x4 ) |
IO_FIELD(R_MMU_KBASE_LO, base_5, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_4, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_3, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_2, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_1, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_0, 0x0 ) );
#else
/* This code is for the corrected Etrax-100 LX version 2... */
#define CACHED_BOOTROM (KSEG_A | 0x08000000UL)
*R_MMU_KSEG = ( IO_STATE(R_MMU_KSEG, seg_f, seg ) | /* cached flash */
IO_STATE(R_MMU_KSEG, seg_e, seg ) | /* uncached flash */
IO_STATE(R_MMU_KSEG, seg_d, page ) | /* vmalloc area */
IO_STATE(R_MMU_KSEG, seg_c, seg ) | /* kernel area */
IO_STATE(R_MMU_KSEG, seg_b, seg ) | /* kernel reg area */
IO_STATE(R_MMU_KSEG, seg_a, seg ) | /* bootrom */
IO_STATE(R_MMU_KSEG, seg_9, page ) | /* user area */
IO_STATE(R_MMU_KSEG, seg_8, page ) |
IO_STATE(R_MMU_KSEG, seg_7, page ) |
IO_STATE(R_MMU_KSEG, seg_6, page ) |
IO_STATE(R_MMU_KSEG, seg_5, page ) |
IO_STATE(R_MMU_KSEG, seg_4, page ) |
IO_STATE(R_MMU_KSEG, seg_3, page ) |
IO_STATE(R_MMU_KSEG, seg_2, page ) |
IO_STATE(R_MMU_KSEG, seg_1, page ) |
IO_STATE(R_MMU_KSEG, seg_0, page ) );
*R_MMU_KBASE_HI = ( IO_FIELD(R_MMU_KBASE_HI, base_f, 0x0 ) |
IO_FIELD(R_MMU_KBASE_HI, base_e, 0x8 ) |
IO_FIELD(R_MMU_KBASE_HI, base_d, 0x0 ) |
IO_FIELD(R_MMU_KBASE_HI, base_c, 0x4 ) |
IO_FIELD(R_MMU_KBASE_HI, base_b, 0xb ) |
IO_FIELD(R_MMU_KBASE_HI, base_a, 0x3 ) |
IO_FIELD(R_MMU_KBASE_HI, base_9, 0x0 ) |
IO_FIELD(R_MMU_KBASE_HI, base_8, 0x0 ) );
*R_MMU_KBASE_LO = ( IO_FIELD(R_MMU_KBASE_LO, base_7, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_6, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_5, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_4, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_3, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_2, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_1, 0x0 ) |
IO_FIELD(R_MMU_KBASE_LO, base_0, 0x0 ) );
#endif
*R_MMU_CONTEXT = ( IO_FIELD(R_MMU_CONTEXT, page_id, 0 ) );
/* The MMU has been enabled ever since head.S but just to make
* it totally obvious we do it here as well.
*/
*R_MMU_CTRL = ( IO_STATE(R_MMU_CTRL, inv_excp, enable ) |
IO_STATE(R_MMU_CTRL, acc_excp, enable ) |
IO_STATE(R_MMU_CTRL, we_excp, enable ) );
*R_MMU_ENABLE = IO_STATE(R_MMU_ENABLE, mmu_enable, enable);
/*
* initialize the bad page table and bad page to point
* to a couple of allocated pages
*/
empty_zero_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE);
memset((void *)empty_zero_page, 0, PAGE_SIZE);
/* All pages are DMA'able in Etrax, so put all in the DMA'able zone */
zones_size[0] = ((unsigned long)high_memory - PAGE_OFFSET) >> PAGE_SHIFT;
for (i = 1; i < MAX_NR_ZONES; i++)
zones_size[i] = 0;
/* Use free_area_init_node instead of free_area_init, because the former
* is designed for systems where the DRAM starts at an address substantially
* higher than 0, like us (we start at PAGE_OFFSET). This saves space in the
* mem_map page array.
*/
free_area_init_node(0, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0);
}
/* Initialize remaps of some I/O-ports. It is important that this
* is called before any driver is initialized.
*/
static int
__init init_ioremap(void)
{
/* Give the external I/O-port addresses their values */
#ifdef CONFIG_CRIS_LOW_MAP
/* Simply a linear map (see the KSEG map above in paging_init) */
port_cse1_addr = (volatile unsigned long *)(MEM_CSE1_START |
MEM_NON_CACHEABLE);
port_csp0_addr = (volatile unsigned long *)(MEM_CSP0_START |
MEM_NON_CACHEABLE);
port_csp4_addr = (volatile unsigned long *)(MEM_CSP4_START |
MEM_NON_CACHEABLE);
#else
/* Note that nothing blows up just because we do this remapping
* it's ok even if the ports are not used or connected
* to anything (or connected to a non-I/O thing) */
port_cse1_addr = (volatile unsigned long *)
ioremap((unsigned long)(MEM_CSE1_START | MEM_NON_CACHEABLE), 16);
port_csp0_addr = (volatile unsigned long *)
ioremap((unsigned long)(MEM_CSP0_START | MEM_NON_CACHEABLE), 16);
port_csp4_addr = (volatile unsigned long *)
ioremap((unsigned long)(MEM_CSP4_START | MEM_NON_CACHEABLE), 16);
#endif
return 0;
}
__initcall(init_ioremap);
/* Helper function for the two below */
static inline void
flush_etrax_cacherange(void *startadr, int length)
{
/* CACHED_BOOTROM is mapped to the boot-rom area (cached) which
* we can use to get fast dummy-reads of cachelines
*/
volatile short *flushadr = (volatile short *)(((unsigned long)startadr & ~PAGE_MASK) |
CACHED_BOOTROM);
length = length > 8192 ? 8192 : length; /* No need to flush more than cache size */
while(length > 0) {
*flushadr; /* dummy read to flush */
flushadr += (32/sizeof(short)); /* a cacheline is 32 bytes */
length -= 32;
}
}
/* Due to a bug in Etrax100(LX) all versions, receiving DMA buffers
* will occasionally corrupt certain CPU writes if the DMA buffers
* happen to be hot in the cache.
*
* As a workaround, we have to flush the relevant parts of the cache
* before (re) inserting any receiving descriptor into the DMA HW.
*/
void
prepare_rx_descriptor(struct etrax_dma_descr *desc)
{
flush_etrax_cacherange((void *)desc->buf, desc->sw_len ? desc->sw_len : 65536);
}
/* Do the same thing but flush the entire cache */
void
flush_etrax_cache(void)
{
flush_etrax_cacherange(0, 8192);
}

View File

@ -1,179 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/cris/arch-v10/mm/tlb.c
*
* Low level TLB handling
*
*
* Copyright (C) 2000-2007 Axis Communications AB
*
* Authors: Bjorn Wesen (bjornw@axis.com)
*
*/
#include <linux/mm_types.h>
#include <asm/tlb.h>
#include <asm/mmu_context.h>
#include <arch/svinto.h>
#define D(x)
/* The TLB can host up to 64 different mm contexts at the same time.
* The running context is R_MMU_CONTEXT, and each TLB entry contains a
* page_id that has to match to give a hit. In page_id_map, we keep track
* of which mm's we have assigned which page_id's, so that we know when
* to invalidate TLB entries.
*
* The last page_id is never running - it is used as an invalid page_id
* so we can make TLB entries that will never match.
*
* Notice that we need to make the flushes atomic, otherwise an interrupt
* handler that uses vmalloced memory might cause a TLB load in the middle
* of a flush causing.
*/
/* invalidate all TLB entries */
void
flush_tlb_all(void)
{
int i;
unsigned long flags;
/* the vpn of i & 0xf is so we dont write similar TLB entries
* in the same 4-way entry group. details...
*/
local_irq_save(flags);
for(i = 0; i < NUM_TLB_ENTRIES; i++) {
*R_TLB_SELECT = ( IO_FIELD(R_TLB_SELECT, index, i) );
*R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) |
IO_FIELD(R_TLB_HI, vpn, i & 0xf ) );
*R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) |
IO_STATE(R_TLB_LO, valid, no ) |
IO_STATE(R_TLB_LO, kernel,no ) |
IO_STATE(R_TLB_LO, we, no ) |
IO_FIELD(R_TLB_LO, pfn, 0 ) );
}
local_irq_restore(flags);
D(printk("tlb: flushed all\n"));
}
/* invalidate the selected mm context only */
void
flush_tlb_mm(struct mm_struct *mm)
{
int i;
int page_id = mm->context.page_id;
unsigned long flags;
D(printk("tlb: flush mm context %d (%p)\n", page_id, mm));
if(page_id == NO_CONTEXT)
return;
/* mark the TLB entries that match the page_id as invalid.
* here we could also check the _PAGE_GLOBAL bit and NOT flush
* global pages. is it worth the extra I/O ?
*/
local_irq_save(flags);
for(i = 0; i < NUM_TLB_ENTRIES; i++) {
*R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i);
if (IO_EXTRACT(R_TLB_HI, page_id, *R_TLB_HI) == page_id) {
*R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) |
IO_FIELD(R_TLB_HI, vpn, i & 0xf ) );
*R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) |
IO_STATE(R_TLB_LO, valid, no ) |
IO_STATE(R_TLB_LO, kernel,no ) |
IO_STATE(R_TLB_LO, we, no ) |
IO_FIELD(R_TLB_LO, pfn, 0 ) );
}
}
local_irq_restore(flags);
}
/* invalidate a single page */
void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
{
struct mm_struct *mm = vma->vm_mm;
int page_id = mm->context.page_id;
int i;
unsigned long flags;
D(printk("tlb: flush page %p in context %d (%p)\n", addr, page_id, mm));
if(page_id == NO_CONTEXT)
return;
addr &= PAGE_MASK; /* perhaps not necessary */
/* invalidate those TLB entries that match both the mm context
* and the virtual address requested
*/
local_irq_save(flags);
for(i = 0; i < NUM_TLB_ENTRIES; i++) {
unsigned long tlb_hi;
*R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i);
tlb_hi = *R_TLB_HI;
if (IO_EXTRACT(R_TLB_HI, page_id, tlb_hi) == page_id &&
(tlb_hi & PAGE_MASK) == addr) {
*R_TLB_HI = IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) |
addr; /* same addr as before works. */
*R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) |
IO_STATE(R_TLB_LO, valid, no ) |
IO_STATE(R_TLB_LO, kernel,no ) |
IO_STATE(R_TLB_LO, we, no ) |
IO_FIELD(R_TLB_LO, pfn, 0 ) );
}
}
local_irq_restore(flags);
}
/*
* Initialize the context related info for a new mm_struct
* instance.
*/
int
init_new_context(struct task_struct *tsk, struct mm_struct *mm)
{
mm->context.page_id = NO_CONTEXT;
return 0;
}
/* called in schedule() just before actually doing the switch_to */
void switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
if (prev != next) {
/* make sure we have a context */
get_mmu_context(next);
/* remember the pgd for the fault handlers
* this is similar to the pgd register in some other CPU's.
* we need our own copy of it because current and active_mm
* might be invalid at points where we still need to derefer
* the pgd.
*/
per_cpu(current_pgd, smp_processor_id()) = next->pgd;
/* switch context in the MMU */
D(printk(KERN_DEBUG "switching mmu_context to %d (%p)\n",
next->context, next));
*R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT,
page_id, next->context.page_id);
}
}

View File

@ -1,2 +0,0 @@
/* At the time of this writing, there's no equivalent ld option. */
OUTPUT_ARCH (cris)

View File

@ -1,211 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
if ETRAX_ARCH_V32
source arch/cris/arch-v32/mach-fs/Kconfig
source arch/cris/arch-v32/mach-a3/Kconfig
source drivers/cpufreq/Kconfig
config ETRAX_DRAM_VIRTUAL_BASE
hex
depends on ETRAX_ARCH_V32
default "c0000000"
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_PORT4
bool "Serial-4"
depends on ETRAX_SERIAL_PORTS = 5
help
Use serial port 4 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 default 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.
endif

View File

@ -1,263 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
if ETRAX_ARCH_V32
config ETRAX_ETHERNET
bool "Ethernet support"
depends on ETRAX_ARCH_V32 && NETDEVICES
select MII
help
This option enables the ETRAX FS built-in 10/100Mbit Ethernet
controller.
config ETRAX_NO_PHY
bool "PHY not present"
depends on ETRAX_ETHERNET
help
This option disables all MDIO communication with an ethernet
transceiver connected to the MII interface. This option shall
typically be enabled if the MII interface is connected to a
switch. This option should normally be disabled. If enabled,
speed and duplex will be locked to 100 Mbit and full duplex.
config ETRAXFS_SERIAL
bool "Serial-port support"
depends on ETRAX_ARCH_V32
select SERIAL_CORE
select SERIAL_CORE_CONSOLE
help
Enables the ETRAX FS serial driver for ser0 (ttyS0)
You probably want this enabled.
config ETRAX_RS485
bool "RS-485 support"
depends on ETRAXFS_SERIAL
help
Enables support for RS-485 serial communication.
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.
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.
config ETRAX_SERIAL_PORT1
bool "Serial port 1 enabled"
depends on ETRAXFS_SERIAL
help
Enables the ETRAX FS serial driver for ser1 (ttyS1).
config ETRAX_SERIAL_PORT2
bool "Serial port 2 enabled"
depends on ETRAXFS_SERIAL
help
Enables the ETRAX FS serial driver for ser2 (ttyS2).
config ETRAX_SERIAL_PORT3
bool "Serial port 3 enabled"
depends on ETRAXFS_SERIAL
help
Enables the ETRAX FS serial driver for ser3 (ttyS3).
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 && ETRAXFS
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_AXISFLASHMAP
bool "Axis flash-map support"
depends on ETRAX_ARCH_V32
select MTD
select MTD_CFI
select MTD_CFI_AMDSTD
select MTD_JEDECPROBE
select MTD_BLOCK
select MTD_COMPLEX_MAPPINGS
select MTD_MTDRAM
help
This option enables MTD mapping of flash devices. Needed to use
flash memories. If unsure, say Y.
config ETRAX_AXISFLASHMAP_MTD0WHOLE
bool "MTD0 is whole boot flash device"
depends on ETRAX_AXISFLASHMAP
help
When this option is not set, mtd0 refers to the first partition
on the boot flash device. When set, mtd0 refers to the whole
device, with mtd1 referring to the first partition etc.
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
help
This option enables MTD mapping of NAND flash devices. Needed to use
NAND flash memories. If unsure, say Y.
config ETRAX_NANDBOOT
bool "Boot from NAND flash"
depends on ETRAX_NANDFLASH
help
This options enables booting from NAND flash devices.
Say Y if your boot code, kernel and root file system is in
NAND flash. Say N if they are in NOR flash.
config ETRAX_CARDBUS
bool "Cardbus support"
depends on ETRAX_ARCH_V32
help
Enabled the ETRAX Cardbus driver.
config PCI
bool
depends on ETRAX_CARDBUS
default y
select HAVE_GENERIC_DMA_COHERENT
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.
config ETRAX_MMC_IOP
tristate "MMC/SD host driver using IO-processor"
depends on ETRAX_ARCH_V32 && MMC
help
This option enables the SD/MMC host controller interface.
The host controller is implemented using the built in
IO-Processor. Only the SPU is used in this implementation.
config ETRAX_SPI_MMC
# Make this one of several "choices" (possible simultaneously but
# suggested uniquely) when an IOP driver emerges for "real" MMC/SD
# protocol support.
tristate
depends on !ETRAX_MMC_IOP
default MMC
select SPI
select MMC_SPI
# While the board info is MMC_SPI only, the drivers are written to be
# independent of MMC_SPI, so we'll keep SPI non-dependent on the
# MMC_SPI config choices (well, except for a single depends-on-line
# for the board-info file until a separate non-MMC SPI board file
# emerges).
# FIXME: When that happens, we'll need to be able to ask for and
# configure non-MMC SPI ports together with MMC_SPI ports (if multiple
# SPI ports are enabled).
config SPI_ETRAX_SSER
tristate
depends on SPI_MASTER && ETRAX_ARCH_V32
select SPI_BITBANG
help
This enables using an synchronous serial (sser) port as a
SPI master controller on Axis ETRAX FS and later. The
driver can be configured to use any sser port.
config SPI_ETRAX_GPIO
tristate
depends on SPI_MASTER && ETRAX_ARCH_V32
select SPI_BITBANG
help
This enables using GPIO pins port as a SPI master controller
on Axis ETRAX FS and later. The driver can be configured to
use any GPIO pins.
config ETRAX_SPI_SSER0
tristate "SPI using synchronous serial port 0 (sser0)"
depends on ETRAX_SPI_MMC
default m if MMC_SPI=m
default y if MMC_SPI=y
default y if MMC_SPI=n
select SPI_ETRAX_SSER
help
Say Y for an MMC/SD socket connected to synchronous serial port 0,
or for devices using the SPI protocol on that port. Say m if you
want to build it as a module, which will be named spi_crisv32_sser.
(You need to select MMC separately.)
config ETRAX_SPI_SSER1
tristate "SPI using synchronous serial port 1 (sser1)"
depends on ETRAX_SPI_MMC
default m if MMC_SPI=m && ETRAX_SPI_SSER0=n
default y if MMC_SPI=y && ETRAX_SPI_SSER0=n
default y if MMC_SPI=n && ETRAX_SPI_SSER0=n
select SPI_ETRAX_SSER
help
Say Y for an MMC/SD socket connected to synchronous serial port 1,
or for devices using the SPI protocol on that port. Say m if you
want to build it as a module, which will be named spi_crisv32_sser.
(You need to select MMC separately.)
config ETRAX_SPI_GPIO
tristate "Bitbanged SPI using gpio pins"
depends on ETRAX_SPI_MMC
select SPI_ETRAX_GPIO
default m if MMC_SPI=m && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n
default y if MMC_SPI=y && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n
default y if MMC_SPI=n && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n
help
Say Y for an MMC/SD socket connected to general I/O pins (but not
a complete synchronous serial ports), or for devices using the SPI
protocol on general I/O pins. Slow and slows down the system.
Say m to build it as a module, which will be called spi_crisv32_gpio.
(You need to select MMC separately.)
endif

View File

@ -1,12 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for Etrax-specific drivers
#
obj-$(CONFIG_ETRAX_STREAMCOPROC) += cryptocop.o
obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o
obj-$(CONFIG_ETRAXFS) += mach-fs/
obj-$(CONFIG_CRIS_MACH_ARTPEC3) += mach-a3/
obj-$(CONFIG_ETRAX_IOP_FW_LOAD) += iop_fw_load.o
obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o
obj-$(CONFIG_PCI) += pci/

View File

@ -1,592 +0,0 @@
/*
* Physical mapping layer for MTD using the Axis partitiontable format
*
* Copyright (c) 2001-2007 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.
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.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/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
#define PAGESIZE (512)
#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 __u32
#endif
/* From head.S */
extern unsigned long romfs_in_flash; /* 1 when romfs_start, _length in flash */
extern unsigned long romfs_start, romfs_length;
extern unsigned long nand_boot; /* 1 when booted from nand flash */
struct partition_name {
char name[6];
};
/* 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
};
#define MAX_PARTITIONS 7
#ifdef CONFIG_ETRAX_NANDBOOT
#define NUM_DEFAULT_PARTITIONS 4
#define DEFAULT_ROOTFS_PARTITION_NO 2
#define DEFAULT_MEDIA_SIZE 0x2000000 /* 32 megs */
#else
#define NUM_DEFAULT_PARTITIONS 3
#define DEFAULT_ROOTFS_PARTITION_NO (-1)
#define DEFAULT_MEDIA_SIZE 0x800000 /* 8 megs */
#endif
#if (MAX_PARTITIONS < NUM_DEFAULT_PARTITIONS)
#error MAX_PARTITIONS must be >= than NUM_DEFAULT_PARTITIONS
#endif
/* 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
},
};
/* If no partition-table was found, we use this default-set.
* Default flash size is 8MB (NOR). CONFIG_ETRAX_PTABLE_SECTOR is most
* likely the size of one flash block and "filesystem"-partition needs
* to be >=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 = 10 * CONFIG_ETRAX_PTABLE_SECTOR,
.offset = CONFIG_ETRAX_PTABLE_SECTOR
},
#define FILESYSTEM_SECTOR (11 * CONFIG_ETRAX_PTABLE_SECTOR)
#ifdef CONFIG_ETRAX_NANDBOOT
{
.name = "rootfs",
.size = 10 * CONFIG_ETRAX_PTABLE_SECTOR,
.offset = FILESYSTEM_SECTOR
},
#undef FILESYSTEM_SECTOR
#define FILESYSTEM_SECTOR (21 * CONFIG_ETRAX_PTABLE_SECTOR)
#endif
{
.name = "rwfs",
.size = DEFAULT_MEDIA_SIZE - FILESYSTEM_SECTOR,
.offset = FILESYSTEM_SECTOR
}
};
#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
/* Main flash device */
static struct mtd_partition main_partition = {
.name = "main",
.size = 0,
.offset = 0
};
#endif
/* Auxiliary partition if we find another flash */
static struct mtd_partition aux_partition = {
.name = "aux",
.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_CFI
mtd_cs = do_map_probe("cfi_probe", map_cs);
#endif
#ifdef CONFIG_MTD_JEDECPROBE
if (!mtd_cs)
mtd_cs = do_map_probe("jedec_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 boundaries.
*
* 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_total;
struct mtd_info *mtds[2];
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;
if (!mtd_cse0 && !mtd_cse1) {
/* No chip found. */
return NULL;
}
if (count > 1) {
/* 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");
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;
return mtd_total;
}
/*
* 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 *main_mtd;
struct mtd_info *aux_mtd = NULL;
int err = 0;
int pidx = 0;
struct partitiontable_head *ptable_head = NULL;
struct partitiontable_entry *ptable;
int ptable_ok = 0;
static char page[PAGESIZE];
size_t len;
int ram_rootfs_partition = -1; /* -1 => no RAM rootfs partition */
int part;
struct mtd_partition *partition;
/* We need a root fs. If it resides in RAM, we need to use an
* MTDRAM device, so it must be enabled in the kernel config,
* but its size must be configured as 0 so as not to conflict
* with our usage.
*/
#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0)
if (!romfs_in_flash && !nand_boot) {
printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM "
"device; configure CONFIG_MTD_MTDRAM with size = 0!\n");
panic("This kernel cannot boot from RAM!\n");
}
#endif
main_mtd = flash_probe();
if (main_mtd)
printk(KERN_INFO "%s: 0x%08llx bytes of NOR flash memory.\n",
main_mtd->name, main_mtd->size);
#ifdef CONFIG_ETRAX_NANDFLASH
aux_mtd = crisv32_nand_flash_probe();
if (aux_mtd)
printk(KERN_INFO "%s: 0x%08x bytes of NAND flash memory.\n",
aux_mtd->name, aux_mtd->size);
#ifdef CONFIG_ETRAX_NANDBOOT
{
struct mtd_info *tmp_mtd;
printk(KERN_INFO "axisflashmap: Set to boot from NAND flash, "
"making NAND flash primary device.\n");
tmp_mtd = main_mtd;
main_mtd = aux_mtd;
aux_mtd = tmp_mtd;
}
#endif /* CONFIG_ETRAX_NANDBOOT */
#endif /* CONFIG_ETRAX_NANDFLASH */
if (!main_mtd && !aux_mtd) {
/* 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");
}
#if 0 /* Dump flash memory so we can see what is going on */
if (main_mtd) {
int sectoraddr;
for (sectoraddr = 0; sectoraddr < 2*65536+4096;
sectoraddr += PAGESIZE) {
main_mtd->read(main_mtd, sectoraddr, PAGESIZE, &len,
page);
printk(KERN_INFO
"Sector at %d (length %d):\n",
sectoraddr, len);
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, page, PAGESIZE, false);
}
}
#endif
if (main_mtd) {
loff_t ptable_sector = CONFIG_ETRAX_PTABLE_SECTOR;
main_mtd->owner = THIS_MODULE;
axisflash_mtd = main_mtd;
/* First partition (rescue) is always set to the default. */
pidx++;
#ifdef CONFIG_ETRAX_NANDBOOT
/* We know where the partition table should be located,
* it will be in first good block after that.
*/
int blockstat;
do {
blockstat = mtd_block_isbad(main_mtd, ptable_sector);
if (blockstat < 0)
ptable_sector = 0; /* read error */
else if (blockstat)
ptable_sector += main_mtd->erasesize;
} while (blockstat && ptable_sector);
#endif
if (ptable_sector) {
mtd_read(main_mtd, ptable_sector, PAGESIZE, &len,
page);
ptable_head = &((struct partitiontable *) page)->head;
}
#if 0 /* Dump partition table so we can see what is going on */
printk(KERN_INFO
"axisflashmap: flash read %d bytes at 0x%08x, data: %8ph\n",
len, CONFIG_ETRAX_PTABLE_SECTOR, page);
printk(KERN_INFO
"axisflashmap: partition table offset %d, data: %8ph\n",
PARTITION_TABLE_OFFSET, page + PARTITION_TABLE_OFFSET);
#endif
}
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.
*/
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 "axisflashmap: "
"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 with 0xffffffff.
*/
while (ptable_ok
&& ptable->offset != PARTITIONTABLE_END_MARKER
&& ptable < max_addr
&& pidx < MAX_PARTITIONS - 1) {
axis_partitions[pidx].offset = offset + ptable->offset;
#ifdef CONFIG_ETRAX_NANDFLASH
if (main_mtd->type == MTD_NANDFLASH) {
axis_partitions[pidx].size =
(((ptable+1)->offset ==
PARTITIONTABLE_END_MARKER) ?
main_mtd->size :
((ptable+1)->offset + offset)) -
(ptable->offset + offset);
} else
#endif /* CONFIG_ETRAX_NANDFLASH */
axis_partitions[pidx].size = ptable->size;
#ifdef CONFIG_ETRAX_NANDBOOT
/* Save partition number of jffs2 ro partition.
* Needed if RAM booting or root file system in RAM.
*/
if (!nand_boot &&
ram_rootfs_partition < 0 && /* not already set */
ptable->type == PARTITION_TYPE_JFFS2 &&
(ptable->flags & PARTITION_FLAGS_READONLY_MASK) ==
PARTITION_FLAGS_READONLY)
ram_rootfs_partition = pidx;
#endif /* CONFIG_ETRAX_NANDBOOT */
pidx++;
ptable++;
}
}
/* Decide whether to use default partition table. */
/* Only use default table if we actually have a device (main_mtd) */
partition = &axis_partitions[0];
if (main_mtd && !ptable_ok) {
memcpy(axis_partitions, axis_default_partitions,
sizeof(axis_default_partitions));
pidx = NUM_DEFAULT_PARTITIONS;
ram_rootfs_partition = DEFAULT_ROOTFS_PARTITION_NO;
}
/* Add artificial partitions for rootfs if necessary */
if (romfs_in_flash) {
/* rootfs is in directly accessible flash memory = NOR flash.
Add an overlapping device for the rootfs partition. */
printk(KERN_INFO "axisflashmap: Adding partition for "
"overlapping root file system image\n");
axis_partitions[pidx].size = romfs_length;
axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR;
axis_partitions[pidx].name = "romfs";
axis_partitions[pidx].mask_flags |= MTD_WRITEABLE;
ram_rootfs_partition = -1;
pidx++;
} else if (romfs_length && !nand_boot) {
/* romfs exists in memory, but not in flash, so must be in RAM.
* Configure an MTDRAM partition. */
if (ram_rootfs_partition < 0) {
/* None set yet, put it at the end */
ram_rootfs_partition = pidx;
pidx++;
}
printk(KERN_INFO "axisflashmap: Adding partition for "
"root file system image in RAM\n");
axis_partitions[ram_rootfs_partition].size = romfs_length;
axis_partitions[ram_rootfs_partition].offset = romfs_start;
axis_partitions[ram_rootfs_partition].name = "romfs";
axis_partitions[ram_rootfs_partition].mask_flags |=
MTD_WRITEABLE;
}
#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
if (main_mtd) {
main_partition.size = main_mtd->size;
err = mtd_device_register(main_mtd, &main_partition, 1);
if (err)
panic("axisflashmap: Could not initialize "
"partition for whole main mtd device!\n");
}
#endif
/* Now, register all partitions with mtd.
* We do this one at a time so we can slip in an MTDRAM device
* in the proper place if required. */
for (part = 0; part < pidx; part++) {
if (part == ram_rootfs_partition) {
/* add MTDRAM partition here */
struct mtd_info *mtd_ram;
mtd_ram = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
if (!mtd_ram)
panic("axisflashmap: Couldn't allocate memory "
"for mtd_info!\n");
printk(KERN_INFO "axisflashmap: Adding RAM partition "
"for rootfs image.\n");
err = mtdram_init_device(mtd_ram,
(void *)(u_int32_t)partition[part].offset,
partition[part].size,
partition[part].name);
if (err)
panic("axisflashmap: Could not initialize "
"MTD RAM device!\n");
/* JFFS2 likes to have an erasesize. Keep potential
* JFFS2 rootfs happy by providing one. Since image
* was most likely created for main mtd, use that
* erasesize, if available. Otherwise, make a guess. */
mtd_ram->erasesize = (main_mtd ? main_mtd->erasesize :
CONFIG_ETRAX_PTABLE_SECTOR);
} else {
err = mtd_device_register(main_mtd, &partition[part],
1);
if (err)
panic("axisflashmap: Could not add mtd "
"partition %d\n", part);
}
}
if (aux_mtd) {
aux_partition.size = aux_mtd->size;
err = mtd_device_register(aux_mtd, &aux_partition, 1);
if (err)
panic("axisflashmap: Could not initialize "
"aux mtd device!\n");
}
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

@ -1,230 +0,0 @@
/*
* 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 <hwregs/reg_rdwr.h>
#include <hwregs/reg_map.h>
#include <hwregs/iop/iop_reg_space.h>
#include <hwregs/iop/iop_mpu_macros.h>
#include <hwregs/iop/iop_mpu_defs.h>
#include <hwregs/iop/iop_spu_defs.h>
#include <hwregs/iop/iop_sw_cpu_defs.h>
#define IOP_TIMEOUT 100
#error "This driver is broken with regard to its driver core usage."
#error "Please contact <greg@kroah.com> for details on how to fix it properly."
static struct device iop_spu_device[2] = {
{ .init_name = "iop-spu0", },
{ .init_name = "iop-spu1", },
};
static struct device iop_mpu_device = {
.init_name = "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)
{
#if 0
/*
* static struct devices can not be added directly to sysfs by ignoring
* the driver model infrastructure. To fix this properly, please use
* the platform_bus to register these devices to be able to properly
* use the firmware infrastructure.
*/
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);
#endif
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

@ -1,5 +0,0 @@
#
# Makefile for Etrax-specific drivers
#
obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o

View File

@ -1,177 +0,0 @@
/*
* arch/cris/arch-v32/drivers/nandflash.c
*
* Copyright (c) 2007
*
* Derived from drivers/mtd/nand/spia.c
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <arch/memmap.h>
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/pio_defs.h>
#include <pinmux.h>
#include <asm/io.h>
#define MANUAL_ALE_CLE_CONTROL 1
#define regf_ALE a0
#define regf_CLE a1
#define regf_NCE ce0_n
#define CLE_BIT 10
#define ALE_BIT 11
#define CE_BIT 12
struct mtd_info_wrapper {
struct nand_chip chip;
};
/* Bitmask for control pins */
#define PIN_BITMASK ((1 << CE_BIT) | (1 << CLE_BIT) | (1 << ALE_BIT))
static struct mtd_info *crisv32_mtd;
/*
* hardware specific access to control-lines
*/
static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
unsigned long flags;
reg_pio_rw_dout dout;
struct nand_chip *this = mtd_to_nand(mtd);
local_irq_save(flags);
/* control bits change */
if (ctrl & NAND_CTRL_CHANGE) {
dout = REG_RD(pio, regi_pio, rw_dout);
dout.regf_NCE = (ctrl & NAND_NCE) ? 0 : 1;
#if !MANUAL_ALE_CLE_CONTROL
if (ctrl & NAND_ALE) {
/* A0 = ALE high */
this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio,
regi_pio, rw_io_access1);
} else if (ctrl & NAND_CLE) {
/* A1 = CLE high */
this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio,
regi_pio, rw_io_access2);
} else {
/* A1 = CLE and A0 = ALE low */
this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio,
regi_pio, rw_io_access0);
}
#else
dout.regf_CLE = (ctrl & NAND_CLE) ? 1 : 0;
dout.regf_ALE = (ctrl & NAND_ALE) ? 1 : 0;
#endif
REG_WR(pio, regi_pio, rw_dout, dout);
}
/* command to chip */
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
local_irq_restore(flags);
}
/*
* read device ready pin
*/
static int crisv32_device_ready(struct mtd_info *mtd)
{
reg_pio_r_din din = REG_RD(pio, regi_pio, r_din);
return din.rdy;
}
/*
* Main initialization routine
*/
struct mtd_info *__init crisv32_nand_flash_probe(void)
{
void __iomem *read_cs;
void __iomem *write_cs;
struct mtd_info_wrapper *wrapper;
struct nand_chip *this;
int err = 0;
reg_pio_rw_man_ctrl man_ctrl = {
.regf_NCE = regk_pio_yes,
#if MANUAL_ALE_CLE_CONTROL
.regf_ALE = regk_pio_yes,
.regf_CLE = regk_pio_yes
#endif
};
reg_pio_rw_oe oe = {
.regf_NCE = regk_pio_yes,
#if MANUAL_ALE_CLE_CONTROL
.regf_ALE = regk_pio_yes,
.regf_CLE = regk_pio_yes
#endif
};
reg_pio_rw_dout dout = { .regf_NCE = 1 };
/* Allocate pio pins to pio */
crisv32_pinmux_alloc_fixed(pinmux_pio);
/* Set up CE, ALE, CLE (ce0_n, a0, a1) for manual control and output */
REG_WR(pio, regi_pio, rw_man_ctrl, man_ctrl);
REG_WR(pio, regi_pio, rw_dout, dout);
REG_WR(pio, regi_pio, rw_oe, oe);
/* Allocate memory for MTD device structure and private data */
wrapper = kzalloc(sizeof(struct mtd_info_wrapper), GFP_KERNEL);
if (!wrapper) {
printk(KERN_ERR "Unable to allocate CRISv32 NAND MTD "
"device structure.\n");
err = -ENOMEM;
return NULL;
}
read_cs = write_cs = (void __iomem *)REG_ADDR(pio, regi_pio,
rw_io_access0);
/* Get pointer to private data */
this = &wrapper->chip;
crisv32_mtd = nand_to_mtd(this);
/* Set address of NAND IO lines */
this->IO_ADDR_R = read_cs;
this->IO_ADDR_W = write_cs;
this->cmd_ctrl = crisv32_hwcontrol;
this->dev_ready = crisv32_device_ready;
/* 20 us command delay time */
this->chip_delay = 20;
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
/* Enable the following for a flash based bad block table */
/* this->bbt_options = NAND_BBT_USE_FLASH; */
/* Scan to find existence of the device */
if (nand_scan(crisv32_mtd, 1)) {
err = -ENXIO;
goto out_mtd;
}
return crisv32_mtd;
out_mtd:
kfree(wrapper);
return NULL;
}

View File

@ -1,5 +0,0 @@
#
# Makefile for Etrax-specific drivers
#
obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o

View File

@ -1,171 +0,0 @@
/*
* 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)
*
* 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/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <arch/memmap.h>
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/gio_defs.h>
#include <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
struct mtd_info_wrapper {
struct nand_chip chip;
};
/* Bitmask for control pins */
#define PIN_BITMASK ((1 << CE_BIT) | (1 << CLE_BIT) | (1 << ALE_BIT))
/* Bitmask for mtd nand control bits */
#define CTRL_BITMASK (NAND_NCE | NAND_CLE | NAND_ALE)
static struct mtd_info *crisv32_mtd;
/*
* hardware specific access to control-lines
*/
static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
unsigned long flags;
reg_gio_rw_pa_dout dout;
struct nand_chip *this = mtd_to_nand(mtd);
local_irq_save(flags);
/* control bits change */
if (ctrl & NAND_CTRL_CHANGE) {
dout = REG_RD(gio, regi_gio, rw_pa_dout);
dout.data &= ~PIN_BITMASK;
#if (CE_BIT == 4 && NAND_NCE == 1 && \
CLE_BIT == 5 && NAND_CLE == 2 && \
ALE_BIT == 6 && NAND_ALE == 4)
/* Pins in same order as control bits, but shifted.
* Optimize for this case; works for 2.6.18 */
dout.data |= ((ctrl & CTRL_BITMASK) ^ NAND_NCE) << CE_BIT;
#else
/* the slow way */
if (!(ctrl & NAND_NCE))
dout.data |= (1 << CE_BIT);
if (ctrl & NAND_CLE)
dout.data |= (1 << CLE_BIT);
if (ctrl & NAND_ALE)
dout.data |= (1 << ALE_BIT);
#endif
REG_WR(gio, regi_gio, rw_pa_dout, dout);
}
/* command to chip */
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
local_irq_restore(flags);
}
/*
* read device ready pin
*/
static 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 mtd_info_wrapper *wrapper;
struct nand_chip *this;
int err = 0;
/* Allocate memory for MTD device structure and private data */
wrapper = kzalloc(sizeof(struct mtd_info_wrapper), GFP_KERNEL);
if (!wrapper) {
printk(KERN_ERR "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(KERN_ERR "CRISv32 NAND ioremap failed\n");
err = -EIO;
goto out_mtd;
}
/* Get pointer to private data */
this = &wrapper->chip;
crisv32_mtd = nand_to_mtd(this);
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);
/* Set address of NAND IO lines */
this->IO_ADDR_R = read_cs;
this->IO_ADDR_W = write_cs;
this->cmd_ctrl = crisv32_hwcontrol;
this->dev_ready = crisv32_device_ready;
/* 20 us command delay time */
this->chip_delay = 20;
this->ecc.mode = NAND_ECC_SOFT;
this->ecc.algo = NAND_ECC_HAMMING;
/* Enable the following for a flash based bad block table */
/* this->bbt_options = NAND_BBT_USE_FLASH; */
/* Scan to find existence 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(wrapper);
return NULL;
}

View File

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

View File

@ -1,74 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/pci.h>
#include <linux/kernel.h>
#include <hwregs/intr_vect.h>
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);
}
resource_size_t
pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
{
resource_size_t start = res->start;
if ((res->flags & IORESOURCE_IO) && (start & 0x300))
start = (start + 0x3ff) & ~0x3ff;
return 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;
if (!dev->msi_enabled)
pcibios_enable_irq(dev);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the linux kernel.
#
extra-y := head.o
obj-y := entry.o traps.o irq.o debugport.o \
process.o ptrace.o setup.o signal.o traps.o time.o \
cache.o cacheflush.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

@ -1,34 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <asm/io.h>
#include <arch/cache.h>
#include <arch/hwregs/dma.h>
/* This file is used to workaround a cache bug, Guinness TR 106. */
inline void flush_dma_descr(struct dma_descr_data *descr, int flush_buf)
{
/* Flush descriptor to make sure we get correct in_eop and after. */
asm volatile ("ftagd [%0]" :: "r" (descr));
/* Flush buffer pointed out by descriptor. */
if (flush_buf)
cris_flush_cache_range(phys_to_virt((unsigned)descr->buf),
(unsigned)(descr->after - descr->buf));
}
EXPORT_SYMBOL(flush_dma_descr);
void flush_dma_list(struct dma_descr_data *descr)
{
while (1) {
flush_dma_descr(descr, 1);
if (descr->eol)
break;
descr = phys_to_virt((unsigned)descr->next);
}
}
EXPORT_SYMBOL(flush_dma_list);
/* From cacheflush.S */
EXPORT_SYMBOL(cris_flush_cache);
/* From cacheflush.S */
EXPORT_SYMBOL(cris_flush_cache_range);

View File

@ -1,100 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
.global cris_flush_cache_range
.type cris_flush_cache_range, @function
cris_flush_cache_range:
move.d 1024, $r12
cmp.d $r11, $r12
bhi cris_flush_1KB
nop
add.d $r10, $r11
ftagd [$r10]
cris_flush_last:
addq 32, $r10
cmp.d $r11, $r10
blt cris_flush_last
ftagd [$r10]
ret
nop
cris_flush_1KB:
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ftagd [$r10]
addq 32, $r10
ba cris_flush_cache_range
sub.d $r12, $r11
.size cris_flush_cache_range, . - cris_flush_cache_range
.global cris_flush_cache
.type cris_flush_cache, @function
cris_flush_cache:
moveq 0, $r10
cris_flush_line:
move.d 16*1024, $r11
addq 16, $r10
cmp.d $r10, $r11
blt cris_flush_line
fidxd [$r10]
ret
nop
.size cris_flush_cache, . - cris_flush_cache

View File

@ -1,26 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/irq.h>
#include <arch/dma.h>
#include <arch/intmem.h>
#include <mach/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_alloc_fixed);
EXPORT_SYMBOL(crisv32_pinmux_dealloc);
EXPORT_SYMBOL(crisv32_pinmux_dealloc_fixed);
/* Functions masking/unmasking interrupts */
EXPORT_SYMBOL(crisv32_mask_irq);
EXPORT_SYMBOL(crisv32_unmask_irq);

View File

@ -1,232 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2003, Axis Communications AB.
*/
#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/reg_map.h>
#include <hwregs/ser_defs.h>
#include <hwregs/dma_defs.h>
#include <mach/pinmux.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
},
#if CONFIG_ETRAX_SERIAL_PORTS == 5
{
4,
regi_ser4,
0,
115200,
'N',
8
},
#endif
};
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];
#elif defined(CONFIG_ETRAX_KGDB_PORT4)
&ports[4];
#else
NULL;
#endif
#endif
static void start_port(struct dbg_port *p)
{
/* 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};
if (!p || 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);
#if CONFIG_ETRAX_SERIAL_PORTS == 5
else if (p->nbr == 4)
crisv32_pinmux_alloc_fixed(pinmux_ser4);
#endif
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);
}
#ifdef CONFIG_ETRAX_KGDB
/* Use polling to get a single character from the kernel debug port */
int getDebugChar(void)
{
reg_ser_rs_stat_din stat;
reg_ser_rw_ack_intr ack_intr = { 0 };
do {
stat = REG_RD(ser, kgdb_port->instance, rs_stat_din);
} while (!stat.dav);
/* Ack the data_avail interrupt. */
ack_intr.dav = 1;
REG_WR(ser, kgdb_port->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_stat_din stat;
do {
stat = REG_RD(ser, kgdb_port->instance, r_stat_din);
} while (!stat.tr_rdy);
REG_WR_INT(ser, kgdb_port->instance, rw_dout, val);
}
#endif /* CONFIG_ETRAX_KGDB */
static void __init early_putch(int c)
{
reg_ser_r_stat_din stat;
/* 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, c);
}
static void __init
early_console_write(struct console *con, const char *s, unsigned n)
{
extern void reset_watchdog(void);
int i;
/* Send data. */
for (i = 0; i < n; i++) {
/* TODO: the '\n' -> '\n\r' translation should be done at the
receiver. Remove it when the serial driver removes it. */
if (s[i] == '\n')
early_putch('\r');
early_putch(s[i]);
reset_watchdog();
}
}
static struct console early_console_dev __initdata = {
.name = "early",
.write = early_console_write,
.flags = CON_PRINTBUFFER | CON_BOOT,
.index = -1
};
/* Register console for printk's, etc. */
int __init init_etrax_debug(void)
{
start_port(port);
/* Register an early console if a debug port was chosen. */
register_console(&early_console_dev);
#ifdef CONFIG_ETRAX_KGDB
start_port(kgdb_port);
#endif /* CONFIG_ETRAX_KGDB */
return 0;
}

View File

@ -1,909 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* 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/linkage.h>
#include <linux/sys.h>
#include <asm/unistd.h>
#include <asm/errno.h>
#include <asm/thread_info.h>
#include <asm/asm-offsets.h>
#include <hwregs/asm/reg_map_asm.h>
#include <hwregs/asm/intr_vect_defs_asm.h>
;; Exported functions.
.globl system_call
.globl ret_from_intr
.globl ret_from_fork
.globl ret_from_kernel_thread
.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.
.type ret_from_fork,@function
ret_from_fork:
jsr schedule_tail
nop
ba ret_from_sys_call
nop
.size ret_from_fork, . - ret_from_fork
.type ret_from_kernel_thread,@function
ret_from_kernel_thread:
jsr schedule_tail
nop
move.d $r2, $r10
jsr $r1
nop
moveq 0, $r9 ; no syscall restarts, TYVM...
ba ret_from_sys_call
nop
.size ret_from_kernel_thread, . - ret_from_kernel_thread
.type ret_from_intr,@function
ret_from_intr:
moveq 0, $r9 ; not a syscall
;; 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
.size ret_from_intr, . - ret_from_intr + 2 ; +2 includes the dslot.
; Note that di below is in delay slot.
.type _resume_userspace,@function
_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
.size _resume_userspace, . - _resume_userspace
;; 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..
.type system_call,@function
system_call:
;; Stack-frame similar to the irq heads, which is reversed in
;; ret_from_sys_call.
sub.d 92, $sp ; Skip EDA.
movem $r13, [$sp]
move.d $sp, $r8
addq 14*4, $r8
move.d $acr, $r0
move $srs, $r1
move $mof, $r2
move $spc, $r3
move $ccs, $r4
move $srp, $r5
move $erp, $r6
move.d $r9, $r7 ; Store syscall number in EXS
subq 4, $sp
movem $r7, [$r8]
ei ; Enable interrupts while processing syscalls.
move.d $r10, [$sp]
; 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 respectively. 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
.size system_call, . - system_call
.type _Rexit,@function
_Rexit:
#if defined(CONFIG_TRACE_IRQFLAGS)
addoq +PT_ccs, $sp, $acr
move.d [$acr], $r0
btstq 15, $r0 ; I1
bpl 1f
nop
jsr trace_hardirqs_on
nop
1:
#endif
;; 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.
.size _Rexit, . - _Rexit
;; We get here after doing a syscall if extra work might need to be done
;; perform syscall exit tracing if needed.
.type _syscall_exit_work,@function
_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
.size _syscall_exit_work, . - _syscall_exit_work
.type _work_pending,@function
_work_pending:
addoq +TI_flags, $r0, $acr
move.d [$acr], $r12 ; The thread_info_flags parameter.
move.d $sp, $r11 ; The regs param.
jsr do_work_pending
move.d $r9, $r10 ; The syscall/irq param.
ba _Rexit
nop
.size _work_pending, . - _work_pending
;; 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.
.type resume,@function
resume:
subq 4, $sp ; Make space for srp.
add.d $r12, $r10 ; R10 = current tasks tss.
addoq +THREAD_ccs, $r10, $acr
move $srp, [$sp] ; Keep old/new PC on the stack.
move $ccs, [$acr] ; Save IRQ enable state.
di
addoq +THREAD_usp, $r10, $acr
subq 10*4, $sp ; Make room for R9.
move $usp, [$acr] ; Save user-mode stackpointer.
;; See copy_thread for the reason why register R9 is saved.
movem $r9, [$sp] ; Save non-scratch registers and R9.
addoq +THREAD_ksp, $r10, $acr
move.d $sp, $r10 ; Return last running task in R10.
move.d $sp, [$acr] ; Save kernel SP for old task.
and.d -8192, $r10 ; Get thread_info from stackpointer.
addoq +TI_task, $r10, $acr
add.d $r12, $r11 ; Find the new tasks tss.
move.d [$acr], $r10 ; Get task.
addoq +THREAD_ksp, $r11, $acr
move.d [$acr], $sp ; Switch to new stackframe.
addoq +THREAD_usp, $r11, $acr
movem [$sp+], $r9 ; Restore non-scratch registers and R9.
move [$acr], $usp ; Restore user-mode stackpointer.
addoq +THREAD_ccs, $r11, $acr
move.d [$sp+], $r11
jump $r11 ; Restore PC.
move [$acr], $ccs ; Restore IRQ enable status.
.size resume, . - resume
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 priority fashion. The
;; hardware will call the unserved interrupts after the handler
;; finishes.
.type multiple_interrupt, @function
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
.size multiple_interrupt, . - multiple_interrupt
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 sys_old_readdir
.long sys_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_ni_syscall /* Old 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
.long sys_ni_syscall /* 285 */ /* available */
.long sys_add_key
.long sys_request_key
.long sys_keyctl
.long sys_ioprio_set
.long sys_ioprio_get /* 290 */
.long sys_inotify_init
.long sys_inotify_add_watch
.long sys_inotify_rm_watch
.long sys_migrate_pages
.long sys_openat /* 295 */
.long sys_mkdirat
.long sys_mknodat
.long sys_fchownat
.long sys_futimesat
.long sys_fstatat64 /* 300 */
.long sys_unlinkat
.long sys_renameat
.long sys_linkat
.long sys_symlinkat
.long sys_readlinkat /* 305 */
.long sys_fchmodat
.long sys_faccessat
.long sys_pselect6
.long sys_ppoll
.long sys_unshare /* 310 */
.long sys_set_robust_list
.long sys_get_robust_list
.long sys_splice
.long sys_sync_file_range
.long sys_tee /* 315 */
.long sys_vmsplice
.long sys_move_pages
.long sys_getcpu
.long sys_epoll_pwait
.long sys_utimensat /* 320 */
.long sys_signalfd
.long sys_timerfd_create
.long sys_eventfd
.long sys_fallocate
.long sys_timerfd_settime /* 325 */
.long sys_timerfd_gettime
.long sys_signalfd4
.long sys_eventfd2
.long sys_epoll_create1
.long sys_dup3 /* 330 */
.long sys_pipe2
.long sys_inotify_init1
.long sys_preadv
.long sys_pwritev
.long sys_setns /* 335 */
.long sys_name_to_handle_at
.long sys_open_by_handle_at
.long sys_rt_tgsigqueueinfo
.long sys_perf_event_open
.long sys_recvmmsg /* 340 */
.long sys_accept4
.long sys_fanotify_init
.long sys_fanotify_mark
.long sys_prlimit64
.long sys_clock_adjtime /* 345 */
.long sys_syncfs
.long sys_sendmmsg
.long sys_process_vm_readv
.long sys_process_vm_writev
.long sys_kcmp /* 350 */
.long sys_finit_module
.long sys_sched_setattr
.long sys_sched_getattr
.long sys_renameat2
.long sys_seccomp /* 355 */
.long sys_getrandom
.long sys_memfd_create
.long sys_bpf
.long sys_execveat
/*
* 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

@ -1,793 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/cris/kernel/fasttimer.c
*
* Fast timers for ETRAX FS
*
* Copyright (C) 2000-2006 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 <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/timer_defs.h>
#include <asm/fasttimer.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.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
static int sanity_failed;
#endif
#define D1(x)
#define D2(x)
#define DP(x)
static unsigned int fast_timer_running;
static unsigned int fast_timers_added;
static unsigned int fast_timers_started;
static unsigned int fast_timers_expired;
static unsigned int fast_timers_deleted;
static unsigned int fast_timer_is_init;
static unsigned int fast_timer_ints;
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 unsigned int debug_log_cnt;
static unsigned int debug_log_cnt_wrapped;
#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];
struct work_struct fast_work;
static void
timer_trig_handler(struct work_struct *work);
/* Not true gettimeofday, only checks the jiffies (uptime) + useconds */
inline void do_gettimeofday_fast(struct fasttime_t *tv)
{
tv->tv_jiff = jiffies;
tv->tv_usec = GET_JIFFIES_USEC();
}
inline int fasttime_cmp(struct fasttime_t *t0, struct fasttime_t *t1)
{
/* Compare jiffies. Takes care of wrapping */
if (time_before(t0->tv_jiff, t1->tv_jiff))
return -1;
else if (time_after(t0->tv_jiff, t1->tv_jiff))
return 1;
/* Compare us */
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 */
inline void 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_time0;
reg_timer_r_time r_time1;
unsigned char trig_wrap;
unsigned char time_wrap;
r_time0 = REG_RD(timer, regi_timer0, 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_timer0, rw_intr_mask);
intr_mask.trig = 0;
REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask);
/* Set timer values and check if trigger wraps. */
/* r_time is 100MHz (10 ns resolution) */
trig_wrap = (trig = r_time0 + delay_us*(1000/10)) < r_time0;
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_timer0, rw_ack_intr, ack_intr);
/* Start timer */
REG_WR(timer, regi_timer0, rw_trig, trig);
trig_cfg.tmr = regk_timer_time;
REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg);
/* Check if we have already passed the trig time */
r_time1 = REG_RD(timer, regi_timer0, r_time);
time_wrap = r_time1 < r_time0;
if ((trig_wrap && !time_wrap) || (r_time1 < trig)) {
/* No, Enable trig irq */
intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask);
intr_mask.trig = 1;
REG_WR(timer, regi_timer0, 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_timer0, rw_trig_cfg, trig_cfg);
REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr);
/* call the int routine */
INIT_WORK(&fast_work, timer_trig_handler);
schedule_work(&fast_work);
}
}
/* 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;
#ifdef FAST_TIMER_SANITY_CHECKS
/* Check so this is not in the list already... */
while (tmp != NULL) {
if (tmp == t) {
printk(KERN_DEBUG
"timer name: %s data: 0x%08lX already "
"in list!\n", name, data);
sanity_failed++;
goto done;
} else
tmp = tmp->next;
}
tmp = fast_timer_list;
#endif
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_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ;
if (t->tv_expires.tv_usec > 1000000) {
t->tv_expires.tv_usec -= 1000000;
t->tv_expires.tv_jiff += HZ;
}
#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 || fasttime_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 &&
fasttime_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));
done:
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)
{
reg_timer_r_masked_intr masked_intr;
/* Check if the timer interrupt is for us (a trig int) */
masked_intr = REG_RD(timer, regi_timer0, r_masked_intr);
if (!masked_intr.trig)
return IRQ_NONE;
timer_trig_handler(NULL);
return IRQ_HANDLED;
}
static void timer_trig_handler(struct work_struct *work)
{
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;
fast_timer_function_type *f;
unsigned long d;
unsigned long flags;
/* We keep interrupts disabled not only when we modify the
* fast timer list, but any time we hold a reference to a
* timer in the list, since del_fast_timer may be called
* from (another) interrupt context. Thus, the only time
* when interrupts are enabled is when calling the timer
* callback function.
*/
local_irq_save(flags);
/* Clear timer trig interrupt */
intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask);
intr_mask.trig = 0;
REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask);
/* First stop timer, then ack interrupt */
/* Stop timer */
trig_cfg.tmr = regk_timer_off;
REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg);
/* Ack interrupt */
ack_intr.trig = 1;
REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr);
fast_timer_running = 0;
fast_timer_ints++;
t = fast_timer_list;
while (t) {
struct fasttime_t tv;
/* Has it really expired? */
do_gettimeofday_fast(&tv);
D1(printk(KERN_DEBUG
"t: %is %06ius\n", tv.tv_jiff, tv.tv_usec));
if (fasttime_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 */
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;
/* Save function callback data before enabling
* interrupts, since the timer may be removed and we
* don't know how it was allocated (e.g. ->function
* and ->data may become overwritten after deletion
* if the timer was stack-allocated).
*/
f = t->function;
d = t->data;
if (f != NULL) {
/* Run the callback function with interrupts
* enabled. */
local_irq_restore(flags);
f(d);
local_irq_save(flags);
} 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"));
}
t = fast_timer_list;
if (t != NULL) {
/* Start next timer.. */
long us = 0;
struct fasttime_t tv;
do_gettimeofday_fast(&tv);
/* time_after_eq takes care of wrapping */
if (time_after_eq(t->tv_expires.tv_jiff, tv.tv_jiff))
us = ((t->tv_expires.tv_jiff - tv.tv_jiff) *
1000000 / HZ + 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);
}
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)
{
wait_queue_head_t *sleep_wait_p = (wait_queue_head_t*)data;
wake_up(sleep_wait_p);
}
/* Useful API */
void schedule_usleep(unsigned long us)
{
struct fast_timer t;
wait_queue_head_t sleep_wait;
init_waitqueue_head(&sleep_wait);
D1(printk("schedule_usleep(%d)\n", us));
start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us,
"usleep");
/* Uninterruptible sleep on the fast timer. (The condition is
* somewhat redundant since the timer is what wakes us up.) */
wait_event(sleep_wait, !fast_timer_pending(&t));
D1(printk("done schedule_usleep(%d)\n", us));
}
#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_show(struct seq_file *m, void *v)
{
unsigned long flags;
int i = 0;
int num_to_show;
struct fasttime_t tv;
struct fast_timer *t, *nextt;
do_gettimeofday_fast(&tv);
seq_printf(m, "Fast timers added: %i\n", fast_timers_added);
seq_printf(m, "Fast timers started: %i\n", fast_timers_started);
seq_printf(m, "Fast timer interrupts: %i\n", fast_timer_ints);
seq_printf(m, "Fast timers expired: %i\n", fast_timers_expired);
seq_printf(m, "Fast timers deleted: %i\n", fast_timers_deleted);
seq_printf(m, "Fast timer running: %s\n",
fast_timer_running ? "yes" : "no");
seq_printf(m, "Current time: %lu.%06lu\n",
(unsigned long)tv.tv_jiff,
(unsigned long)tv.tv_usec);
#ifdef FAST_TIMER_SANITY_CHECKS
seq_printf(m, "Sanity failed: %i\n", sanity_failed);
#endif
seq_putc(m, '\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)) {
seq_printf(m, debug_log_string[i], debug_log_value[i]);
if (seq_has_overflowed(m))
return 0;
i = (i+1) % DEBUG_LOG_MAX;
}
}
seq_putc(m, '\n');
#endif
num_to_show = (fast_timers_started < NUM_TIMER_STATS ? fast_timers_started:
NUM_TIMER_STATS);
seq_printf(m, "Timers started: %i\n", fast_timers_started);
for (i = 0; i < num_to_show; i++) {
int cur = (fast_timers_started - i - 1) % NUM_TIMER_STATS;
#if 1 //ndef FAST_TIMER_LOG
seq_printf(m, "div: %i delay: %i\n",
timer_div_settings[cur],
timer_delay_settings[cur]);
#endif
#ifdef FAST_TIMER_LOG
t = &timer_started_log[cur];
seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 0x%08lX\n",
t->name,
(unsigned long)t->tv_set.tv_jiff,
(unsigned long)t->tv_set.tv_usec,
(unsigned long)t->tv_expires.tv_jiff,
(unsigned long)t->tv_expires.tv_usec,
t->delay_us,
t->data);
if (seq_has_overflowed(m))
return 0;
#endif
}
seq_putc(m, '\n');
#ifdef FAST_TIMER_LOG
num_to_show = (fast_timers_added < NUM_TIMER_STATS ? fast_timers_added:
NUM_TIMER_STATS);
seq_printf(m, "Timers added: %i\n", fast_timers_added);
for (i = 0; i < num_to_show; i++) {
t = &timer_added_log[(fast_timers_added - i - 1) % NUM_TIMER_STATS];
seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 0x%08lX\n",
t->name,
(unsigned long)t->tv_set.tv_jiff,
(unsigned long)t->tv_set.tv_usec,
(unsigned long)t->tv_expires.tv_jiff,
(unsigned long)t->tv_expires.tv_usec,
t->delay_us,
t->data);
if (seq_has_overflowed(m))
return 0;
}
seq_putc(m, '\n');
num_to_show = (fast_timers_expired < NUM_TIMER_STATS ? fast_timers_expired:
NUM_TIMER_STATS);
seq_printf(m, "Timers expired: %i\n", fast_timers_expired);
for (i = 0; i < num_to_show; i++){
t = &timer_expired_log[(fast_timers_expired - i - 1) % NUM_TIMER_STATS];
seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 0x%08lX\n",
t->name,
(unsigned long)t->tv_set.tv_jiff,
(unsigned long)t->tv_set.tv_usec,
(unsigned long)t->tv_expires.tv_jiff,
(unsigned long)t->tv_expires.tv_usec,
t->delay_us,
t->data);
if (seq_has_overflowed(m))
return 0;
}
seq_putc(m, '\n');
#endif
seq_puts(m, "Active timers:\n");
local_irq_save(flags);
t = fast_timer_list;
while (t != NULL){
nextt = t->next;
local_irq_restore(flags);
seq_printf(m, "%-14s s: %6lu.%06lu e: %6lu.%06lu d: %6li us data: 0x%08lX\n",
t->name,
(unsigned long)t->tv_set.tv_jiff,
(unsigned long)t->tv_set.tv_usec,
(unsigned long)t->tv_expires.tv_jiff,
(unsigned long)t->tv_expires.tv_usec,
t->delay_us,
t->data);
if (seq_has_overflowed(m))
return 0;
local_irq_save(flags);
if (t->next != nextt)
printk("timer removed!\n");
t = nextt;
}
local_irq_restore(flags);
return 0;
}
static int proc_fasttimer_open(struct inode *inode, struct file *file)
{
return single_open_size(file, proc_fasttimer_show, PDE_DATA(inode), BIG_BUF_SIZE);
}
static const struct file_operations proc_fasttimer_fops = {
.open = proc_fasttimer_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#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 fasttime_t 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 fasttime_t 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(KERN_DEBUG "fast_timer_test() %is %06i\n", tv.tv_jiff, tv.tv_usec);
for (j = 0; j < 1000; j++)
{
printk(KERN_DEBUG "%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(KERN_DEBUG "%i.%i %i.%i %i.%i %i.%i %i.%i\n",
tv_exp[j].tv_jiff, tv_exp[j].tv_usec,
tv_exp[j+1].tv_jiff, tv_exp[j+1].tv_usec,
tv_exp[j+2].tv_jiff, tv_exp[j+2].tv_usec,
tv_exp[j+3].tv_jiff, tv_exp[j+3].tv_usec,
tv_exp[j+4].tv_jiff, 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(KERN_INFO "Timers started %is %06i\n",
tv0.tv_jiff, tv0.tv_usec);
printk(KERN_INFO "Timers started at %is %06i\n",
tv1.tv_jiff, tv1.tv_usec);
printk(KERN_INFO "Timers done %is %06i\n",
tv2.tv_jiff, 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_jiff,
t->tv_set.tv_usec,
t->tv_expires.tv_jiff,
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_jiff,
tv_exp[j].tv_usec,
exp_num[j],
(tv_exp[j].tv_jiff - t->tv_expires.tv_jiff) *
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
int 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
proc_create("fasttimer", 0, NULL, &proc_fasttimer_fops);
#endif /* PROC_FS */
if (request_irq(TIMER0_INTR_VECT, timer_trig_interrupt,
IRQF_SHARED,
"fast timer int", &fast_timer_list))
printk(KERN_ERR "err: fasttimer irq\n");
fast_timer_is_init = 1;
#ifdef FAST_TIMER_TEST
printk("do test\n");
fast_timer_test();
#endif
}
return 0;
}
__initcall(fast_timer_init);

View File

@ -1,439 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* CRISv32 kernel startup code.
*
* Copyright (C) 2003, Axis Communications AB
*/
#include <linux/init.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 <arch/memmap.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/intr_vect.h>
#include <hwregs/asm/mmu_defs_asm.h>
#include <hwregs/asm/reg_map_asm.h>
#include <mach/startup.inc>
#define CRAMFS_MAGIC 0x28cd3d45
#define JHEAD_MAGIC 0x1FF528A6
#define JHEAD_SIZE 8
#define RAM_INIT_MAGIC 0x56902387
#define COMMAND_LINE_MAGIC 0x87109563
#define NAND_BOOT_MAGIC 0x9a9db001
;; 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 nand_boot
.global swapper_pg_dir
__HEAD
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
SETUP_WAIT_STATES
GIO_INIT
;; 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.
#ifdef CONFIG_CRIS_MACH_ARTPEC3
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_d, 5) \
| REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0
#else
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
#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.
;; ARTPEC-3:
;; c,d used for linear kernel mapping, up to 512 MB
;; e used for vmalloc
;; f unused, but page mapped to get page faults
;; ETRAX FS:
;; c used for linear kernel mapping, up to 256 MB
;; d used for vmalloc
;; e,f used for memory-mapped NOR flash
#ifdef CONFIG_CRIS_MACH_ARTPEC3
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, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_e, page) \
| REG_STATE(mmu, rw_mm_cfg, seg_d, linear) \
| 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, 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
#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
; Check if starting from DRAM (network->RAM boot or unpacked
; compressed kernel), or directly from 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
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.
__INIT
_inflash:
;; Initialize DRAM.
cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized?
beq _dram_initialized
nop
#if defined CONFIG_ETRAXFS
#include "../mach-fs/dram_init.S"
#elif defined CONFIG_CRIS_MACH_ARTPEC3
#include "../mach-a3/dram_init.S"
#else
#error Only ETRAXFS and ARTPEC-3 supported!
#endif
_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.
;; Leave the cramfs file system (piggybacked after the kernel) in flash.
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
;; Check for cramfs.
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
;; Set length and start of cramfs, set romfs_in_flash flag
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; if so, set appropriate flags
;; and move on.
cmp.d NAND_BOOT_MAGIC, $r12
bne move_cramfs ; not nand, jump
moveq 1, $r0
move.d nand_boot, $r1 ; tell axisflashmap we're booting from NAND
move.d $r0, [$r1]
moveq 0, $r0 ; tell axisflashmap romfs is not in
move.d romfs_in_flash, $r1 ; (directly accessed) flash
move.d $r0, [$r1]
jump _start_it ; continue with boot
nop
move_cramfs:
;; kernel is in DRAM.
;; Must figure out if there is a piggybacked rootfs image or not.
;; Set romfs_length to 0 => no rootfs image available by default.
moveq 0, $r0
move.d romfs_length, $r1
move.d $r0, [$r1]
;; The kernel could have been unpacked to DRAM by the loader, but
;; the cramfs image could still be in the flash immediately
;; following the compressed kernel image. The loader passes the address
;; of the byte 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
;; cramfs rootfs might to be in flash. Check for it.
move.d [$r9], $r0 ; cramfs_super.magic
cmp.d CRAMFS_MAGIC, $r0
bne _no_romfs_in_flash
nop
;; found cramfs in flash. set address and size, and romfs_in_flash flag.
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:
;; No romfs in flash, so look for cramfs, or jffs2 with jhead,
;; after kernel in RAM, as is the case with network->RAM boot.
;; For cramfs, partition starts with magic and length.
;; For jffs2, a jhead is prepended which contains with magic and length.
;; The jhead is not part of the jffs2 partition however.
move.d __bss_start, $r0
move.d [$r0], $r1
cmp.d CRAMFS_MAGIC, $r1 ; cramfs magic?
beq 2f ; yes, jump
nop
cmp.d JHEAD_MAGIC, $r1 ; jffs2 (jhead) magic?
bne 4f ; no, skip copy
nop
addq 4, $r0 ; location of jffs2 size
move.d [$r0+], $r2 ; fetch jffs2 size -> r2
; r0 now points to start of jffs2
ba 3f
nop
2:
addoq +4, $r0, $acr ; location of cramfs size
move.d [$acr], $r2 ; fetch cramfs size -> r2
; r0 still points to start of cramfs
3:
;; Now, move the root fs to after kernel's BSS
move.d _end, $r1 ; start of cramfs -> r1
move.d romfs_start, $r3
move.d $r1, [$r3] ; store at romfs_start (for axisflashmap)
move.d romfs_length, $r3
move.d $r2, [$r3] ; store size at romfs_length
add.d $r2, $r0 ; copy from end and downwards
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
4:
;; BSS move done.
;; Clear romfs_in_flash flag, as we now know romfs is in DRAM
;; Also clear nand_boot flag; if we got here, we know we've not
;; booted from NAND flash.
moveq 0, $r0
move.d romfs_in_flash, $r1
move.d $r0, [$r1]
moveq 0, $r0
move.d nand_boot, $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+], $r1
move.b $r1, [$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]
;; 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
; Initialize registers to increase determinism
move.d __bss_start, $r0
movem [$r0], $r13
#ifdef CONFIG_ETRAX_L2CACHE
jsr l2cache_init
nop
#endif
jump start_kernel ; Jump to start_kernel() in init/main.c.
nop
.data
etrax_irv:
.dword 0
; Variables for communication with the Axis flash map driver (axisflashmap),
; and for setting up memory in arch/cris/kernel/setup.c .
; romfs_start is set to the start of the root file system, if it exists
; in directly accessible memory (i.e. NOR Flash when booting from Flash,
; or RAM when booting directly from a network-downloaded RAM image)
romfs_start:
.dword 0
; romfs_length is set to the size of the root file system image, if it exists
; in directly accessible memory (see romfs_start). Otherwise it is set to 0.
romfs_length:
.dword 0
; romfs_in_flash is set to 1 if the root file system resides in directly
; accessible flash memory (i.e. NOR flash). It is set to 0 for RAM boot
; or NAND flash boot.
romfs_in_flash:
.dword 0
; nand_boot is set to 1 when the kernel has been booted from NAND flash
nand_boot:
.dword 0
swapper_pg_dir = 0xc0002000
.section ".init.data", "aw"
#if defined CONFIG_ETRAXFS
#include "../mach-fs/hw_settings.S"
#elif defined CONFIG_CRIS_MACH_ARTPEC3
#include "../mach-a3/hw_settings.S"
#else
#error Only ETRAXFS and ARTPEC-3 supported!
#endif

View File

@ -1,520 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2003, Axis Communications AB.
*/
#include <asm/irq.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/profile.h>
#include <linux/of.h>
#include <linux/of_irq.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 <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/intr_vect.h>
#include <hwregs/intr_vect_defs.h>
#define CPU_FIXED -1
/* IRQ masks (refer to comment for crisv32_do_multiple) */
#if TIMER0_INTR_VECT - FIRST_IRQ < 32
#define TIMER_MASK (1 << (TIMER0_INTR_VECT - FIRST_IRQ))
#undef TIMER_VECT1
#else
#define TIMER_MASK (1 << (TIMER0_INTR_VECT - FIRST_IRQ - 32))
#define TIMER_VECT1
#endif
#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_KGDB_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_REAL_IRQS] =
{ [0 ... NR_REAL_IRQS - 1] = {0, CPU_MASK_ALL} };
static unsigned long irq_regs[NR_CPUS] =
{
regi_irq,
};
#if NR_REAL_IRQS > 32
#define NBR_REGS 2
#else
#define NBR_REGS 1
#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);
/* From traps.c. */
extern void breakh_BUG(void);
/*
* Build the IRQ handler stubs using macros from irq.h.
*/
#ifdef CONFIG_CRIS_MACH_ARTPEC3
BUILD_TIMER_IRQ(0x31, 0)
#else
BUILD_IRQ(0x31)
#endif
BUILD_IRQ(0x32)
BUILD_IRQ(0x33)
BUILD_IRQ(0x34)
BUILD_IRQ(0x35)
BUILD_IRQ(0x36)
BUILD_IRQ(0x37)
BUILD_IRQ(0x38)
BUILD_IRQ(0x39)
BUILD_IRQ(0x3a)
BUILD_IRQ(0x3b)
BUILD_IRQ(0x3c)
BUILD_IRQ(0x3d)
BUILD_IRQ(0x3e)
BUILD_IRQ(0x3f)
BUILD_IRQ(0x40)
BUILD_IRQ(0x41)
BUILD_IRQ(0x42)
BUILD_IRQ(0x43)
BUILD_IRQ(0x44)
BUILD_IRQ(0x45)
BUILD_IRQ(0x46)
BUILD_IRQ(0x47)
BUILD_IRQ(0x48)
BUILD_IRQ(0x49)
BUILD_IRQ(0x4a)
#ifdef CONFIG_ETRAXFS
BUILD_TIMER_IRQ(0x4b, 0)
#else
BUILD_IRQ(0x4b)
#endif
BUILD_IRQ(0x4c)
BUILD_IRQ(0x4d)
BUILD_IRQ(0x4e)
BUILD_IRQ(0x4f)
BUILD_IRQ(0x50)
#if MACH_IRQS > 32
BUILD_IRQ(0x51)
BUILD_IRQ(0x52)
BUILD_IRQ(0x53)
BUILD_IRQ(0x54)
BUILD_IRQ(0x55)
BUILD_IRQ(0x56)
BUILD_IRQ(0x57)
BUILD_IRQ(0x58)
BUILD_IRQ(0x59)
BUILD_IRQ(0x5a)
BUILD_IRQ(0x5b)
BUILD_IRQ(0x5c)
BUILD_IRQ(0x5d)
BUILD_IRQ(0x5e)
BUILD_IRQ(0x5f)
BUILD_IRQ(0x60)
BUILD_IRQ(0x61)
BUILD_IRQ(0x62)
BUILD_IRQ(0x63)
BUILD_IRQ(0x64)
BUILD_IRQ(0x65)
BUILD_IRQ(0x66)
BUILD_IRQ(0x67)
BUILD_IRQ(0x68)
BUILD_IRQ(0x69)
BUILD_IRQ(0x6a)
BUILD_IRQ(0x6b)
BUILD_IRQ(0x6c)
BUILD_IRQ(0x6d)
BUILD_IRQ(0x6e)
BUILD_IRQ(0x6f)
BUILD_IRQ(0x70)
#endif
/* Pointers to the low-level handlers. */
static void (*interrupt[MACH_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, IRQ0x50_interrupt,
#if MACH_IRQS > 32
IRQ0x51_interrupt, IRQ0x52_interrupt, IRQ0x53_interrupt,
IRQ0x54_interrupt, IRQ0x55_interrupt, IRQ0x56_interrupt,
IRQ0x57_interrupt, IRQ0x58_interrupt, IRQ0x59_interrupt,
IRQ0x5a_interrupt, IRQ0x5b_interrupt, IRQ0x5c_interrupt,
IRQ0x5d_interrupt, IRQ0x5e_interrupt, IRQ0x5f_interrupt,
IRQ0x60_interrupt, IRQ0x61_interrupt, IRQ0x62_interrupt,
IRQ0x63_interrupt, IRQ0x64_interrupt, IRQ0x65_interrupt,
IRQ0x66_interrupt, IRQ0x67_interrupt, IRQ0x68_interrupt,
IRQ0x69_interrupt, IRQ0x6a_interrupt, IRQ0x6b_interrupt,
IRQ0x6c_interrupt, IRQ0x6d_interrupt, IRQ0x6e_interrupt,
IRQ0x6f_interrupt, IRQ0x70_interrupt,
#endif
};
void
block_irq(int irq, int cpu)
{
int intr_mask;
unsigned long flags;
spin_lock_irqsave(&irq_lock, flags);
/* Remember, 1 let thru, 0 block. */
if (irq - FIRST_IRQ < 32) {
intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
rw_mask, 0);
intr_mask &= ~(1 << (irq - FIRST_IRQ));
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
0, intr_mask);
} else {
intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
rw_mask, 1);
intr_mask &= ~(1 << (irq - FIRST_IRQ - 32));
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
1, 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);
/* Remember, 1 let thru, 0 block. */
if (irq - FIRST_IRQ < 32) {
intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
rw_mask, 0);
intr_mask |= (1 << (irq - FIRST_IRQ));
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
0, intr_mask);
} else {
intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
rw_mask, 1);
intr_mask |= (1 << (irq - FIRST_IRQ - 32));
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask,
1, 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 (cpumask_test_cpu(cpu, &irq_allocations[irq - FIRST_IRQ].mask))
goto out;
/* IRQ must be moved to another CPU. */
cpu = cpumask_first(&irq_allocations[irq - FIRST_IRQ].mask);
irq_allocations[irq - FIRST_IRQ].cpu = cpu;
out:
spin_unlock_irqrestore(&irq_lock, flags);
return cpu;
}
void crisv32_mask_irq(int irq)
{
int cpu;
for (cpu = 0; cpu < NR_CPUS; cpu++)
block_irq(irq, cpu);
}
void crisv32_unmask_irq(int irq)
{
unblock_irq(irq, irq_cpu(irq));
}
static void enable_crisv32_irq(struct irq_data *data)
{
crisv32_unmask_irq(data->irq);
}
static void disable_crisv32_irq(struct irq_data *data)
{
crisv32_mask_irq(data->irq);
}
static int set_affinity_crisv32_irq(struct irq_data *data,
const struct cpumask *dest, bool force)
{
unsigned long flags;
spin_lock_irqsave(&irq_lock, flags);
irq_allocations[data->irq - FIRST_IRQ].mask = *dest;
spin_unlock_irqrestore(&irq_lock, flags);
return 0;
}
static struct irq_chip crisv32_irq_type = {
.name = "CRISv32",
.irq_shutdown = disable_crisv32_irq,
.irq_enable = enable_crisv32_irq,
.irq_disable = disable_crisv32_irq,
.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 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[NBR_REGS];
int bit;
int i;
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();
for (i = 0; i < NBR_REGS; i++) {
/* Get which IRQs that happened. */
masked[i] = REG_RD_INT_VECT(intr_vect, irq_regs[cpu],
r_masked_vect, i);
/* Calculate new IRQ mask with these IRQs disabled. */
mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i);
mask &= ~masked[i];
/* Timer IRQ is never masked */
#ifdef TIMER_VECT1
if ((i == 1) && (masked[0] & TIMER_MASK))
mask |= TIMER_MASK;
#else
if ((i == 0) && (masked[0] & TIMER_MASK))
mask |= TIMER_MASK;
#endif
/* Block all the IRQs */
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i, mask);
/* Check for timer IRQ and handle it special. */
#ifdef TIMER_VECT1
if ((i == 1) && (masked[i] & TIMER_MASK)) {
masked[i] &= ~TIMER_MASK;
do_IRQ(TIMER0_INTR_VECT, regs);
}
#else
if ((i == 0) && (masked[i] & TIMER_MASK)) {
masked[i] &= ~TIMER_MASK;
do_IRQ(TIMER0_INTR_VECT, regs);
}
#endif
}
#ifdef IGNORE_MASK
/* Remove IRQs that can't be handled as multiple. */
masked[0] &= ~IGNORE_MASK;
#endif
/* Handle the rest of the IRQs. */
for (i = 0; i < NBR_REGS; i++) {
for (bit = 0; bit < 32; bit++) {
if (masked[i] & (1 << bit))
do_IRQ(bit + FIRST_IRQ + i*32, regs);
}
}
/* Unblock all the IRQs. */
for (i = 0; i < NBR_REGS; i++) {
mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i);
mask |= masked[i];
REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i, mask);
}
/* This irq_exit() will trigger the soft IRQs. */
irq_exit();
}
static int crisv32_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw_irq_num)
{
irq_set_chip_and_handler(virq, &crisv32_irq_type, handle_simple_irq);
return 0;
}
static struct irq_domain_ops crisv32_irq_ops = {
.map = crisv32_irq_map,
.xlate = irq_domain_xlate_onecell,
};
/*
* 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};
struct device_node *np;
struct irq_domain *domain;
/* Clear all interrupts masks. */
for (i = 0; i < NBR_REGS; i++)
REG_WR_VECT(intr_vect, regi_irq, rw_mask, i, vect_mask);
for (i = 0; i < 256; i++)
etrax_irv->v[i] = weird_irq;
np = of_find_compatible_node(NULL, NULL, "axis,crisv32-intc");
domain = irq_domain_add_legacy(np, NBR_INTR_VECT - FIRST_IRQ,
FIRST_IRQ, FIRST_IRQ,
&crisv32_irq_ops, NULL);
BUG_ON(!domain);
irq_set_default_host(domain);
of_node_put(np);
for (i = FIRST_IRQ, j = 0; j < NBR_INTR_VECT && j < MACH_IRQS; i++, j++)
set_exception_vector(i, interrupt[j]);
/* Mark Timer and IPI IRQs as CPU local */
irq_allocations[TIMER0_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED;
irq_set_status_flags(TIMER0_INTR_VECT, IRQ_PER_CPU);
irq_allocations[IPI_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED;
irq_set_status_flags(IPI_INTR_VECT, 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);
#ifdef CONFIG_BUG
/* Break 14 handler, used to implement cheap BUG(). */
set_exception_vector(0x1e, breakh_BUG);
#endif
/* 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

@ -1,552 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2004 Axis Communications AB
*
* Code for handling break 8, hardware breakpoint, single step, and serial
* port exceptions for kernel debugging purposes.
*/
#include <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

@ -1,180 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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/sched.h>
#include <linux/sched/debug.h>
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/reg_map.h>
#include <hwregs/timer_defs.h>
#include <hwregs/intr_vect_defs.h>
#include <linux/ptrace.h>
extern void stop_watchdog(void);
/* We use this if we don't have any better idle routine. */
void default_idle(void)
{
local_irq_enable();
/* Halt until exception. */
__asm__ volatile("halt");
}
/*
* Free current thread data structures etc..
*/
extern void deconfigure_bp(long pid);
void exit_thread(struct task_struct *tsk)
{
deconfigure_bp(tsk->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_timer0, rw_wd_ctrl, wd_ctrl);
}
#endif
while (1)
; /* Wait for reset. */
}
/*
* 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);
extern asmlinkage void ret_from_kernel_thread(void);
int
copy_thread(unsigned long clone_flags, unsigned long usp,
unsigned long arg, struct task_struct *p)
{
struct pt_regs *childregs = task_pt_regs(p);
struct switch_stack *swstack = ((struct switch_stack *) childregs) - 1;
/*
* 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.
*/
if (unlikely(p->flags & PF_KTHREAD)) {
memset(swstack, 0,
sizeof(struct switch_stack) + sizeof(struct pt_regs));
swstack->r1 = usp;
swstack->r2 = arg;
childregs->ccs = 1 << (I_CCS_BITNR + CCS_SHIFT);
swstack->return_ip = (unsigned long) ret_from_kernel_thread;
p->thread.ksp = (unsigned long) swstack;
p->thread.usp = 0;
return 0;
}
*childregs = *current_pt_regs(); /* Struct copy of pt_regs. */
childregs->r10 = 0; /* Child returns 0 after a fork/clone. */
/* Set a new TLS ?
* The TLS is in $mof because it is the 5th argument to sys_clone.
*/
if (p->mm && (clone_flags & CLONE_SETTLS)) {
task_thread_info(p)->tls = childregs->mof;
}
/* Put the switch stack right below the pt_regs. */
/* Parameter 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 ?: rdusp();
p->thread.ksp = (unsigned long) swstack;
return 0;
}
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();
show_regs_print_info(KERN_DEFAULT);
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

@ -1,492 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000-2007, Axis Communications AB.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/signal.h>
#include <linux/security.h>
#include <linux/uaccess.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <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 *)task_pt_regs(task))[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 *)task_pt_regs(task))[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))
task_pt_regs(task)->erp = data;
} else if (regno <= PT_MAX)
return put_debugreg(task->pid, regno, data);
else
return -1;
return 0;
}
void user_enable_single_step(struct task_struct *child)
{
unsigned long tmp;
/*
* 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);
}
void user_disable_single_step(struct task_struct *child)
{
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);
}
}
/*
* Called by kernel/ptrace.c when detaching.
*
* Make sure the single step bit is not set.
*/
void
ptrace_disable(struct task_struct *child)
{
/* Deconfigure SPC and S-bit. */
user_disable_single_step(child);
put_reg(child, PT_SPC, 0);
/* Deconfigure any watchpoints associated with the child. */
deconfigure_bp(child->pid);
}
long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
int ret;
unsigned int regno = addr >> 2;
unsigned long __user *datap = (unsigned long __user *)data;
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 = ptrace_access_vm(child, addr, &tmp, sizeof(tmp), FOLL_FORCE);
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) || regno > PT_MAX)
break;
tmp = get_reg(child, regno);
ret = put_user(tmp, datap);
break;
}
/* Write the word at location address. */
case PTRACE_POKETEXT:
case PTRACE_POKEDATA:
ret = generic_ptrace_pokedata(child, addr, data);
break;
/* Write the word at location address in the USER area. */
case PTRACE_POKEUSR:
ret = -EIO;
if ((addr & 3) || regno > PT_MAX)
break;
if (regno == 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, regno, data))
break;
ret = 0;
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:
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), FOLL_FORCE);
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

@ -1,163 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Display CPU info in /proc/cpuinfo.
*
* Copyright (C) 2003, Axis Communications AB.
*/
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/param.h>
#include <linux/i2c.h>
#include <linux/platform_device.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},
#ifdef CONFIG_ETRAXFS
{"ETRAX FS", 32, 32, HAS_ETHERNET100 | HAS_ATA | HAS_MMU},
#else
{"ARTPEC-3", 32, 32, HAS_ETHERNET100 | HAS_MMU},
#endif
{"Unknown", 0, 0, 0}
};
int show_cpuinfo(struct seq_file *m, void *v)
{
int i;
int cpu = (int)v - 1;
unsigned long revision;
struct cpu_info *info;
info = &cpinfo[ARRAY_SIZE(cpinfo) - 1];
revision = rdvr();
for (i = 0; i < ARRAY_SIZE(cpinfo); i++) {
if (cpinfo[i].rev == revision) {
info = &cpinfo[i];
break;
}
}
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);
return 0;
}
#endif /* CONFIG_PROC_FS */
void show_etrax_copyright(void)
{
#ifdef CONFIG_ETRAXFS
printk(KERN_INFO "Linux/CRISv32 port on ETRAX FS "
"(C) 2003, 2004 Axis Communications AB\n");
#else
printk(KERN_INFO "Linux/CRISv32 port on ARTPEC-3 "
"(C) 2003-2009 Axis Communications AB\n");
#endif
}
static struct i2c_board_info __initdata i2c_info[] = {
{I2C_BOARD_INFO("camblock", 0x43)},
{I2C_BOARD_INFO("tmp100", 0x48)},
{I2C_BOARD_INFO("tmp100", 0x4A)},
{I2C_BOARD_INFO("tmp100", 0x4C)},
{I2C_BOARD_INFO("tmp100", 0x4D)},
{I2C_BOARD_INFO("tmp100", 0x4E)},
#ifdef CONFIG_RTC_DRV_PCF8563
{I2C_BOARD_INFO("pcf8563", 0x51)},
#endif
{I2C_BOARD_INFO("pca9536", 0x41)},
{I2C_BOARD_INFO("fnp300", 0x40)},
{I2C_BOARD_INFO("fnp300", 0x42)},
{I2C_BOARD_INFO("adc101", 0x54)},
};
static struct i2c_board_info __initdata i2c_info2[] = {
{I2C_BOARD_INFO("camblock", 0x43)},
{I2C_BOARD_INFO("tmp100", 0x48)},
{I2C_BOARD_INFO("tmp100", 0x4A)},
{I2C_BOARD_INFO("tmp100", 0x4C)},
{I2C_BOARD_INFO("tmp100", 0x4D)},
{I2C_BOARD_INFO("tmp100", 0x4E)},
{I2C_BOARD_INFO("pca9536", 0x41)},
{I2C_BOARD_INFO("fnp300", 0x40)},
{I2C_BOARD_INFO("fnp300", 0x42)},
{I2C_BOARD_INFO("adc101", 0x54)},
};
static struct i2c_board_info __initdata i2c_info3[] = {
{I2C_BOARD_INFO("adc101", 0x54)},
};
static int __init etrax_init(void)
{
i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info));
i2c_register_board_info(1, i2c_info2, ARRAY_SIZE(i2c_info2));
i2c_register_board_info(2, i2c_info3, ARRAY_SIZE(i2c_info3));
return 0;
}
arch_initcall(etrax_init);

View File

@ -1,541 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2003, Axis Communications AB.
*/
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
#include <linux/mm.h>
#include <linux/slab.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 <linux/uaccess.h>
#include <arch/hwregs/cpu_vect.h>
extern unsigned long cris_signal_return_page;
/*
* 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. */
};
void do_signal(int restart, struct pt_regs *regs);
void keep_debug_flags(unsigned long oldccs, unsigned long oldspc,
struct pt_regs *regs);
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->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));
/* Don't perform syscall restarting */
regs->exs = -1;
/* Restore the old USP. */
err |= __get_user(old_usp, &sc->usp);
wrusp(old_usp);
return err;
badframe:
return 1;
}
asmlinkage int sys_sigreturn(void)
{
struct pt_regs *regs = current_pt_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;
set_current_blocked(&set);
if (restore_sigcontext(regs, &frame->sc))
goto badframe;
keep_debug_flags(oldccs, oldspc, regs);
return regs->r10;
badframe:
force_sig(SIGSEGV, current);
return 0;
}
asmlinkage int sys_rt_sigreturn(void)
{
struct pt_regs *regs = current_pt_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;
set_current_blocked(&set);
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
goto badframe;
if (restore_altstack(&frame->uc.uc_stack))
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 ksignal *ksig, size_t frame_size)
{
unsigned long sp = sigsp(rdusp(), ksig);
/* 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 int
setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
{
int err;
unsigned long return_ip;
struct signal_frame __user *frame;
err = 0;
frame = get_sigframe(ksig, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
return -EFAULT;
err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
if (err)
return -EFAULT;
if (_NSIG_WORDS > 1) {
err |= __copy_to_user(frame->extramask, &set->sig[1],
sizeof(frame->extramask));
}
if (err)
return -EFAULT;
/*
* Set up to return from user-space. If provided, use a stub
* already located in user-space.
*/
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
return_ip = (unsigned long)ksig->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)
return -EFAULT;
/*
* Set up registers for signal handler.
*
* Where the code enters now.
* Where the code enter later.
* First argument, signo.
*/
regs->erp = (unsigned long) ksig->ka.sa.sa_handler;
regs->srp = return_ip;
regs->r10 = ksig->sig;
/* Actually move the USP to reflect the stacked frame. */
wrusp((unsigned long)frame);
return 0;
}
static int
setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
{
int err;
unsigned long return_ip;
struct rt_signal_frame __user *frame;
err = 0;
frame = get_sigframe(ksig, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
return -EFAULT;
err |= __put_user(&frame->info, &frame->pinfo);
err |= __put_user(&frame->uc, &frame->puc);
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
if (err)
return -EFAULT;
/* 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));
err |= __save_altstack(&frame->uc.uc_stack, rdusp());
if (err)
return -EFAULT;
/*
* Set up to return from user-space. If provided, use a stub
* already located in user-space.
*/
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
return_ip = (unsigned long) ksig->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)
return -EFAULT;
/*
* 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) ksig->ka.sa.sa_handler;
regs->srp = return_ip;
regs->r10 = ksig->sig;
regs->r11 = (unsigned long) &frame->info;
regs->r12 = 0;
/* Actually move the usp to reflect the stacked frame. */
wrusp((unsigned long)frame);
return 0;
}
/* Invoke a signal handler to, well, handle the signal. */
static inline void
handle_signal(int canrestart, struct ksignal *ksig, struct pt_regs *regs)
{
sigset_t *oldset = sigmask_to_save();
int ret;
/* 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 (!(ksig->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 (ksig->ka.sa.sa_flags & SA_SIGINFO)
ret = setup_rt_frame(ksig, oldset, regs);
else
ret = setup_frame(ksig, oldset, regs);
signal_setup_done(ret, ksig, 0);
}
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*
* 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.
*/
void
do_signal(int canrestart, struct pt_regs *regs)
{
struct ksignal ksig;
canrestart = canrestart && ((int)regs->exs >= 0);
/*
* 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;
if (get_signal(&ksig)) {
/* Whee! Actually deliver the signal. */
handle_signal(canrestart, &ksig, regs);
return;
}
/* 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->r9 = __NR_restart_syscall;
regs->erp -= 2;
}
}
/* if there's no signal to deliver, we just put the saved sigmask
* back */
restore_saved_sigmask();
}
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 within 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 = 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

@ -1,345 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/arch/cris/arch-v32/kernel/time.c
*
* Copyright (C) 2003-2010 Axis Communications AB
*
*/
#include <linux/timex.h>
#include <linux/time.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/swap.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/threads.h>
#include <linux/cpufreq.h>
#include <linux/sched_clock.h>
#include <linux/mm.h>
#include <asm/types.h>
#include <asm/signal.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/irq_regs.h>
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/timer_defs.h>
#include <hwregs/intr_vect_defs.h>
#ifdef CONFIG_CRIS_MACH_ARTPEC3
#include <hwregs/clkgen_defs.h>
#endif
/* Watchdog defines */
#define ETRAX_WD_KEY_MASK 0x7F /* key is 7 bit */
#define ETRAX_WD_HZ 763 /* watchdog counts at 763 Hz */
/* Number of 763 counts before watchdog bites */
#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1)
#define CRISV32_TIMER_FREQ (100000000lu)
unsigned long timer_regs[NR_CPUS] =
{
regi_timer0,
};
extern int set_rtc_mmss(unsigned long nowtime);
#ifdef CONFIG_CPU_FREQ
static int cris_time_freq_notifier(struct notifier_block *nb,
unsigned long val, void *data);
static struct notifier_block cris_time_freq_notifier_block = {
.notifier_call = cris_time_freq_notifier,
};
#endif
unsigned long get_ns_in_jiffie(void)
{
reg_timer_r_tmr0_data data;
unsigned long ns;
data = REG_RD(timer, regi_timer0, r_tmr0_data);
ns = (TIMER0_DIV - data) * 10;
return ns;
}
/* 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 watchdog 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 set this really low. */
#define WATCHDOG_MIN_FREE_PAGES 8
#if defined(CONFIG_ETRAX_WATCHDOG_NICE_DOGGY)
/* for reliable NICE_DOGGY behaviour */
static int bite_in_progress;
#endif
void reset_watchdog(void)
{
#if defined(CONFIG_ETRAX_WATCHDOG)
reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
#if defined(CONFIG_ETRAX_WATCHDOG_NICE_DOGGY)
if (unlikely(bite_in_progress))
return;
#endif
/* 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 */
/* Invert key, which is 7 bits */
watchdog_key ^= ETRAX_WD_KEY_MASK;
wd_ctrl.cnt = ETRAX_WD_CNT;
wd_ctrl.cmd = regk_timer_start;
wd_ctrl.key = watchdog_key;
REG_WR(timer, regi_timer0, 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_timer0, 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;
nmi_enter();
oops_in_progress = 1;
#if defined(CONFIG_ETRAX_WATCHDOG_NICE_DOGGY)
bite_in_progress = 1;
#endif
printk(KERN_WARNING "Watchdog bite\n");
/* Check if forced restart or unexpected watchdog */
if (cause_of_death == 0xbedead) {
#ifdef CONFIG_CRIS_MACH_ARTPEC3
/* There is a bug in Artpec-3 (voodoo TR 78) that requires
* us to go to lower frequency for the reset to be reliable
*/
reg_clkgen_rw_clk_ctrl ctrl =
REG_RD(clkgen, regi_clkgen, rw_clk_ctrl);
ctrl.pll = 0;
REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, ctrl);
#endif
while(1);
}
/* Unexpected watchdog, stop the watchdog and dump registers. */
stop_watchdog();
printk(KERN_WARNING "Oops: bitten by watchdog\n");
show_registers(regs);
oops_in_progress = 0;
printk("\n"); /* Flush mtdoops. */
#ifndef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
reset_watchdog();
#endif
while(1) /* nothing */;
#endif
}
extern void cris_profile_sample(struct pt_regs *regs);
static void __iomem *timer_base;
static int crisv32_clkevt_switch_state(struct clock_event_device *dev)
{
reg_timer_rw_tmr0_ctrl ctrl = {
.op = regk_timer_hold,
.freq = regk_timer_f100,
};
REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
return 0;
}
static int crisv32_clkevt_next_event(unsigned long evt,
struct clock_event_device *dev)
{
reg_timer_rw_tmr0_ctrl ctrl = {
.op = regk_timer_ld,
.freq = regk_timer_f100,
};
REG_WR(timer, timer_base, rw_tmr0_div, evt);
REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
ctrl.op = regk_timer_run;
REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
return 0;
}
static irqreturn_t crisv32_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
reg_timer_rw_tmr0_ctrl ctrl = {
.op = regk_timer_hold,
.freq = regk_timer_f100,
};
reg_timer_rw_ack_intr ack = { .tmr0 = 1 };
reg_timer_r_masked_intr intr;
intr = REG_RD(timer, timer_base, r_masked_intr);
if (!intr.tmr0)
return IRQ_NONE;
REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
REG_WR(timer, timer_base, rw_ack_intr, ack);
reset_watchdog();
#ifdef CONFIG_SYSTEM_PROFILER
cris_profile_sample(get_irq_regs());
#endif
evt->event_handler(evt);
return IRQ_HANDLED;
}
static struct clock_event_device crisv32_clockevent = {
.name = "crisv32-timer",
.rating = 300,
.features = CLOCK_EVT_FEAT_ONESHOT,
.set_state_oneshot = crisv32_clkevt_switch_state,
.set_state_shutdown = crisv32_clkevt_switch_state,
.tick_resume = crisv32_clkevt_switch_state,
.set_next_event = crisv32_clkevt_next_event,
};
/* Timer is IRQF_SHARED so drivers can add stuff to the timer irq chain. */
static struct irqaction irq_timer = {
.handler = crisv32_timer_interrupt,
.flags = IRQF_TIMER | IRQF_SHARED,
.name = "crisv32-timer",
.dev_id = &crisv32_clockevent,
};
static u64 notrace crisv32_timer_sched_clock(void)
{
return REG_RD(timer, timer_base, r_time);
}
static void __init crisv32_timer_init(void)
{
reg_timer_rw_intr_mask timer_intr_mask;
reg_timer_rw_tmr0_ctrl ctrl = {
.op = regk_timer_hold,
.freq = regk_timer_f100,
};
REG_WR(timer, timer_base, rw_tmr0_ctrl, ctrl);
timer_intr_mask = REG_RD(timer, timer_base, rw_intr_mask);
timer_intr_mask.tmr0 = 1;
REG_WR(timer, timer_base, rw_intr_mask, timer_intr_mask);
}
void __init time_init(void)
{
int irq;
int ret;
/* 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;
irq = TIMER0_INTR_VECT;
timer_base = (void __iomem *) regi_timer0;
crisv32_timer_init();
sched_clock_register(crisv32_timer_sched_clock, 32,
CRISV32_TIMER_FREQ);
clocksource_mmio_init(timer_base + REG_RD_ADDR_timer_r_time,
"crisv32-timer", CRISV32_TIMER_FREQ,
300, 32, clocksource_mmio_readl_up);
crisv32_clockevent.cpumask = cpu_possible_mask;
crisv32_clockevent.irq = irq;
ret = setup_irq(irq, &irq_timer);
if (ret)
pr_warn("failed to setup irq %d\n", irq);
clockevents_config_and_register(&crisv32_clockevent,
CRISV32_TIMER_FREQ,
2, 0xffffffff);
/* Enable watchdog if we should use one. */
#if defined(CONFIG_ETRAX_WATCHDOG)
printk(KERN_INFO "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). */
{
unsigned long flags;
local_save_flags(flags);
flags |= (1<<30); /* NMI M flag is at bit 30 */
local_irq_restore(flags);
}
#endif
#ifdef CONFIG_CPU_FREQ
cpufreq_register_notifier(&cris_time_freq_notifier_block,
CPUFREQ_TRANSITION_NOTIFIER);
#endif
}
#ifdef CONFIG_CPU_FREQ
static int cris_time_freq_notifier(struct notifier_block *nb,
unsigned long val, void *data)
{
struct cpufreq_freqs *freqs = data;
if (val == CPUFREQ_POSTCHANGE) {
reg_timer_r_tmr0_data data;
reg_timer_rw_tmr0_div div = (freqs->new * 500) / HZ;
do {
data = REG_RD(timer, timer_regs[freqs->cpu],
r_tmr0_data);
} while (data > 20);
REG_WR(timer, timer_regs[freqs->cpu], rw_tmr0_div, div);
}
return 0;
}
#endif

View File

@ -1,196 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2003-2006, Axis Communications AB.
*/
#include <linux/ptrace.h>
#include <linux/extable.h>
#include <linux/uaccess.h>
#include <linux/sched/debug.h>
#include <hwregs/supp_reg.h>
#include <hwregs/intr_vect_defs.h>
#include <asm/irq.h>
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 process for all cases this
* function is called, and current->thread.usp isn't up to date for the
* current process. Experience shows that using USP is the way to go.
*/
unsigned long usp = rdusp();
unsigned long d_mmu_cause;
unsigned long i_mmu_cause;
printk("CPU: %d\n", smp_processor_id());
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 acr: %08lx\n",
regs->r12, regs->r13, regs->orig_r10, regs->acr);
printk(" sp: %08lx\n", (unsigned long)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);
printk(" Data MMU Cause: %08lx\n", d_mmu_cause);
printk("Instruction MMU Cause: %08lx\n", i_mmu_cause);
printk("Process %s (pid: %d, stackpage=%08lx)\n",
current->comm, current->pid, (unsigned long)current);
/*
* When in-kernel, we also print out the stack and code at the
* time of the fault..
*/
if (!user_mode(regs)) {
int i;
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);
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 parenthesises.
*/
for (i = -12; i < 12; i++) {
unsigned char c;
if (__get_user(c, &((unsigned char *)regs->erp)[i])) {
bad_value:
printk(" Bad IP value.");
break;
}
if (i == 0)
printk("(%02x) ", c);
else
printk("%02x ", c);
}
printk("\n");
}
}
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);
}
extern void (*nmi_handler)(struct pt_regs *);
void handle_nmi(struct pt_regs *regs)
{
#ifdef CONFIG_ETRAXFS
reg_intr_vect_r_nmi r;
#endif
if (nmi_handler)
nmi_handler(regs);
#ifdef CONFIG_ETRAXFS
/* Wait until nmi is no longer active. */
do {
r = REG_RD(intr_vect, regi_irq, r_nmi);
} while (r.ext == regk_intr_vect_on);
#endif
}
#ifdef CONFIG_BUG
extern void die_if_kernel(const char *str, struct pt_regs *regs, long err);
/* Copy of the regs at BUG() time. */
struct pt_regs BUG_regs;
void do_BUG(char *file, unsigned int line)
{
printk("kernel BUG at %s:%d!\n", file, line);
die_if_kernel("Oops", &BUG_regs, 0);
}
EXPORT_SYMBOL(do_BUG);
void fixup_BUG(struct pt_regs *regs)
{
BUG_regs = *regs;
#ifdef CONFIG_DEBUG_BUGVERBOSE
/*
* Fixup the BUG arguments through exception handlers.
*/
{
const struct exception_table_entry *fixup;
/*
* ERP points at the "break 14" + 2, compensate for the 2
* bytes.
*/
fixup = search_exception_tables(instruction_pointer(regs) - 2);
if (fixup) {
/* Adjust the instruction pointer in the stackframe. */
instruction_pointer(regs) = fixup->fixup;
arch_fixup(regs);
}
}
#else
/* Dont try to lookup the filename + line, just dump regs. */
do_BUG("unknown", 0);
#endif
}
/*
* Break 14 handler. Save regs and jump into the fixup_BUG.
*/
__asm__ ( ".text\n\t"
".global breakh_BUG\n\t"
"breakh_BUG:\n\t"
SAVE_ALL
KGDB_FIXUP
"move.d $sp, $r10\n\t"
"jsr fixup_BUG\n\t"
"nop\n\t"
"jump ret_from_intr\n\t"
"nop\n\t");
#ifdef CONFIG_DEBUG_BUGVERBOSE
void
handle_BUG(struct pt_regs *regs)
{
}
#endif
#endif

View File

@ -1,7 +0,0 @@
#
# Makefile for Etrax-specific library files..
#
lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o \
csumcpfruser.o delay.o strcmp.o

View File

@ -1,89 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* A fast checksum routine using movem
* Copyright (c) 1998-2007 Axis Communications AB
*
* csum_partial(const unsigned char * buff, int len, unsigned int sum)
*/
.globl csum_partial
.type csum_partial,@function
csum_partial:
;; r10 - src
;; r11 - length
;; r12 - checksum
;; Optimized for large packets
subq 10*4, $r11
blt _word_loop
move.d $r11, $acr
subq 9*4,$sp
clearf c
movem $r8,[$sp]
;; do a movem checksum
_mloop: movem [$r10+],$r9 ; read 10 longwords
;; Loop count without touching the c flag.
addoq -10*4, $acr, $acr
;; perform dword checksumming on the 10 longwords
addc $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
;; test $acr without trashing carry.
move.d $acr, $acr
bpl _mloop
;; r11 <= acr is not really needed in the mloop, just using the dslot
;; to prepare for what is needed after mloop.
move.d $acr, $r11
;; fold the last carry into r13
addc 0, $r12
movem [$sp+],$r8 ; restore regs
_word_loop:
addq 10*4,$r11 ; compensate for last loop underflowing length
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
_no_fold:
subq 2,$r11
blt _no_words
add.d $r13,$r12 ; checksum += r13
;; checksum the rest of the words
_wloop: subq 2,$r11
bge _wloop
addu.w [$r10+],$r12
_no_words:
addq 2,$r11
;; see if we have one odd byte more
bne _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
.size csum_partial, .-csum_partial

View File

@ -1,95 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* A fast checksum+copy routine using movem
* Copyright (c) 1998-2007 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
.type csum_partial_copy_nocheck,@function
csum_partial_copy_nocheck:
;; r10 - src
;; r11 - dst
;; r12 - length
;; r13 - checksum
;; Optimized for large packets
subq 10*4, $r12
blt _word_loop
move.d $r12, $acr
subq 9*4,$sp
clearf c
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
addoq -10*4, $acr, $acr ; loop counter in latency cycle
movem $r9,[$r11+] ; write 10 longwords
;; perform dword checksumming on the 10 longwords
addc $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
;; test $acr, without trashing carry.
move.d $acr, $acr
bpl _mloop
;; r12 <= acr is needed after mloop and in the exception handlers.
move.d $acr, $r12
;; fold the last carry into r13
addc 0, $r13
movem [$sp+],$r8 ; restore regs
_word_loop:
addq 10*4,$r12 ; compensate for last loop underflowing length
;; 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
subq 2, $r12
blt _no_words
add.d $r9,$r13 ; checksum += r0
;; copy and checksum the rest of the words
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+]
_no_words:
addq 2,$r12
bne _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
.size csum_partial_copy_nocheck, . - csum_partial_copy_nocheck

View File

@ -1,70 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* 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

@ -1,29 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Precise Delay Loops for ETRAX FS
*
* Copyright (C) 2006 Axis Communications AB.
*
*/
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/timer_defs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/module.h>
/*
* On ETRAX FS, we can check the free-running read-only 100MHz timer
* getting 32-bit 10ns precision, theoretically good for 42.94967295
* seconds. Unsigned arithmetic and careful expression handles
* wrapping.
*/
void cris_delay10ns(u32 n10ns)
{
u32 t0 = REG_RD(timer, regi_timer0, r_time);
while (REG_RD(timer, regi_timer0, r_time) - t0 < n10ns)
;
}
EXPORT_SYMBOL(cris_delay10ns);

View File

@ -1,259 +0,0 @@
/* A memset for CRIS.
Copyright (C) 1999-2005 Axis Communications.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Neither the name of Axis Communications nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AXIS COMMUNICATIONS AND ITS CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AXIS
COMMUNICATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
/* FIXME: This file should really only be used for reference, as the
result is somewhat depending on gcc generating what we expect rather
than what we describe. An assembly file should be used instead. */
/* Note the multiple occurrence of the expression "12*4", including the
asm. It is hard to get it into the asm in a good way. Thus better to
expose the problem everywhere: no macro. */
/* Assuming one cycle per dword written or read (ok, not really true; the
world is not ideal), and one cycle 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 MEMSET_BY_BLOCK_THRESHOLD (1 * 48)
/* No name ambiguities in this file. */
__asm__ (".syntax no_register_prefix");
void *memset(void *pdst, int c, unsigned int plen)
{
/* Now we want the parameters in special registers. Make sure the
compiler does something usable with 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. Memsetting about 3..4 bytes or less get
penalized here compared to the generic implementation. */
/* This is fragile performancewise at best. Check with newer GCC
releases, if they compile cascaded "x |= x << 8" to sane code. */
__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) /* Inputs. */
: "0" (lc) /* Outputs. */
: "r13"); /* Trash. */
{
register char *dst __asm__ ("r13") = pdst;
if (((unsigned long) pdst & 3) != 0
/* Oops! n = 0 must be a valid 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;
}
}
/* Decide which setting method to use. */
if (n >= MEMSET_BY_BLOCK_THRESHOLD)
{
/* 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-block sizes
suboptimal. */
__asm__ volatile
("\
;; GCC does promise correct register allocations, but let's \n\
;; make sure it keeps its promises. \n\
.ifnc %0-%1-%4,$r13-$r12-$r11 \n\
.error \"GCC reg alloc bug: %0-%1-%4 != $r13-$r12-$r11\" \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\
"
#ifdef __arch_common_v10_v32
/* Cater to branch offset difference between v32 and v10. We
assume the branch below has an 8-bit offset. */
" setf\n"
#endif
" subq 12*4,r12 \n\
bge 0b \n\
movem r11,[r13+] \n\
\n\
;; Compensate for last loop underflowing n. \n\
addq 12*4,r12 \n\
\n\
;; Restore registers from stack. \n\
movem [sp+],r10"
/* Outputs. */
: "=r" (dst), "=r" (n)
/* Inputs. */
: "0" (dst), "1" (n), "r" (lc));
}
/* An ad-hoc unroll, used for 4*12-1..16 bytes. */
while (n >= 16)
{
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
n -= 16;
}
switch (n)
{
case 0:
break;
case 1:
*dst = (char) lc;
break;
case 2:
*(short *) dst = (short) lc;
break;
case 3:
*(short *) dst = (short) lc; dst += 2;
*dst = (char) lc;
break;
case 4:
*(long *) dst = lc;
break;
case 5:
*(long *) dst = lc; dst += 4;
*dst = (char) lc;
break;
case 6:
*(long *) dst = lc; dst += 4;
*(short *) dst = (short) lc;
break;
case 7:
*(long *) dst = lc; dst += 4;
*(short *) dst = (short) lc; dst += 2;
*dst = (char) lc;
break;
case 8:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc;
break;
case 9:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*dst = (char) lc;
break;
case 10:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(short *) dst = (short) lc;
break;
case 11:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(short *) dst = (short) lc; dst += 2;
*dst = (char) lc;
break;
case 12:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(long *) dst = lc;
break;
case 13:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*dst = (char) lc;
break;
case 14:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(short *) dst = (short) lc;
break;
case 15:
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(long *) dst = lc; dst += 4;
*(short *) dst = (short) lc; dst += 2;
*dst = (char) lc;
break;
}
}
return return_dst;
}

View File

@ -1,21 +0,0 @@
; strcmp.S -- CRISv32 version.
; Copyright (C) 2008 AXIS Communications AB
; Written by Edgar E. Iglesias
;
; This source code is licensed under the GNU General Public License,
; Version 2. See the file COPYING for more details.
.global strcmp
.type strcmp,@function
strcmp:
1:
move.b [$r10+], $r12
seq $r13
sub.b [$r11+], $r12
or.b $r12, $r13
beq 1b
nop
ret
movs.b $r12, $r10
.size strcmp, . - strcmp

View File

@ -1,236 +0,0 @@
/* A memcpy for CRIS.
Copyright (C) 1994-2005 Axis Communications.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Neither the name of Axis Communications nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AXIS COMMUNICATIONS AND ITS CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AXIS
COMMUNICATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
/* FIXME: This file should really only be used for reference, as the
result is somewhat depending on gcc generating what we expect rather
than what we describe. An assembly file should be used instead. */
#include <stddef.h>
/* Break even between movem and move16 is really at 38.7 * 2, but
modulo 44, so up to the next multiple of 44, we use ordinary code. */
#define MEMCPY_BY_BLOCK_THRESHOLD (44 * 2)
/* No name ambiguities in this file. */
__asm__ (".syntax no_register_prefix");
void *
memcpy(void *pdst, const void *psrc, size_t pn)
{
/* 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 unsigned char *dst __asm__ ("r13") = pdst;
register unsigned 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--;
*dst = *src;
src++;
dst++;
}
if ((unsigned long) dst & 2)
{
n -= 2;
*(short *) dst = *(short *) src;
src += 2;
dst += 2;
}
}
/* Decide which copying method to use. */
if (n >= MEMCPY_BY_BLOCK_THRESHOLD)
{
/* 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
("\
;; GCC does promise correct register allocations, but let's \n\
;; make sure it keeps its promises. \n\
.ifnc %0-%1-%2,$r13-$r11-$r12 \n\
.error \"GCC reg alloc bug: %0-%1-%4 != $r13-$r12-$r11\" \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\
"
#ifdef __arch_common_v10_v32
/* Cater to branch offset difference between v32 and v10. We
assume the branch below has an 8-bit offset. */
" setf\n"
#endif
" movem [r11+],r10 \n\
subq 44,r12 \n\
bge 0b \n\
movem r10,[r13+] \n\
\n\
;; Compensate for last loop underflowing n. \n\
addq 44,r12 \n\
\n\
;; Restore registers from stack. \n\
movem [sp+],r10"
/* Outputs. */
: "=r" (dst), "=r" (src), "=r" (n)
/* Inputs. */
: "0" (dst), "1" (src), "2" (n));
}
while (n >= 16)
{
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
n -= 16;
}
switch (n)
{
case 0:
break;
case 1:
*dst = *src;
break;
case 2:
*(short *) dst = *(short *) src;
break;
case 3:
*(short *) dst = *(short *) src; dst += 2; src += 2;
*dst = *src;
break;
case 4:
*(long *) dst = *(long *) src;
break;
case 5:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*dst = *src;
break;
case 6:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(short *) dst = *(short *) src;
break;
case 7:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(short *) dst = *(short *) src; dst += 2; src += 2;
*dst = *src;
break;
case 8:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src;
break;
case 9:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*dst = *src;
break;
case 10:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(short *) dst = *(short *) src;
break;
case 11:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(short *) dst = *(short *) src; dst += 2; src += 2;
*dst = *src;
break;
case 12:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src;
break;
case 13:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*dst = *src;
break;
case 14:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(short *) dst = *(short *) src;
break;
case 15:
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(long *) dst = *(long *) src; dst += 4; src += 4;
*(short *) dst = *(short *) src; dst += 2; src += 2;
*dst = *src;
break;
}
return return_dst;
}

View File

@ -1,458 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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 <linux/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 alright, 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;
}
EXPORT_SYMBOL(__copy_user);
/* Copy from user to kernel. The return-value is the number of bytes that were
inaccessible. */
unsigned long __copy_user_in(void *pdst, const void __user *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 alright, 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 (retn != 0)
goto exception;
}
if (((unsigned long) src & 2) && n >= 2)
{
__asm_copy_from_user_2 (dst, src, retn);
n -= 2;
if (retn != 0)
goto exception;
}
}
/* 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 exception;
}
/* 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;
exception:
return retn + n;
}
EXPORT_SYMBOL(__copy_user_in);
/* 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 alright, 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;
}
EXPORT_SYMBOL(__do_clear_user);

View File

@ -1,111 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
if CRIS_MACH_ARTPEC3
menu "Artpec-3 options"
depends on CRIS_MACH_ARTPEC3
config ETRAX_DRAM_VIRTUAL_BASE
hex
default "c0000000"
config ETRAX_L2CACHE
bool
default y
config ETRAX_SERIAL_PORTS
int
default 5
config ETRAX_DDR2_MRS
hex "DDR2 MRS"
default "0"
config ETRAX_DDR2_TIMING
hex "DDR2 SDRAM timing"
default "0"
help
SDRAM timing parameters.
config ETRAX_DDR2_CONFIG
hex "DDR2 config"
default "0"
config ETRAX_DDR2_LATENCY
hex "DDR2 latency"
default "0"
config ETRAX_PIO_CE0_CFG
hex "PIO CE0 configuration"
default "0"
config ETRAX_PIO_CE1_CFG
hex "PIO CE1 configuration"
default "0"
config ETRAX_PIO_CE2_CFG
hex "PIO CE2 configuration"
default "0"
config ETRAX_DEF_GIO_PA_OE
hex "GIO_PA_OE"
default "00000000"
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"
default "00000000"
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"
default "000000000"
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"
default "000000000"
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"
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"
default "00000"
help
Configures the initial data for the general port C bits. Most
products should use 00000 here.
endmenu
endif

View File

@ -1,8 +0,0 @@
#
# Makefile for the linux kernel.
#
obj-y := dma.o pinmux.o arbiter.o
clean:

View File

@ -1,635 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Memory arbiter functions. Allocates bandwidth through the
* arbiter and sets up arbiter breakpoints.
*
* The algorithm first assigns slots to the clients that has specified
* bandwidth (e.g. ethernet) and then the remaining slots are divided
* on all the active clients.
*
* Copyright (c) 2004-2007 Axis Communications AB.
*
* The artpec-3 has two arbiters. The memory hierarchy looks like this:
*
*
* CPU DMAs
* | |
* | |
* -------------- ------------------
* | foo arbiter|----| Internal memory|
* -------------- ------------------
* |
* --------------
* | L2 cache |
* --------------
* |
* h264 etc |
* | |
* | |
* --------------
* | bar arbiter|
* --------------
* |
* ---------
* | SDRAM |
* ---------
*
*/
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/marb_foo_defs.h>
#include <hwregs/marb_bar_defs.h>
#include <arbiter.h>
#include <hwregs/intr_vect.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <asm/irq_regs.h>
#define D(x)
struct crisv32_watch_entry {
unsigned long instance;
watch_callback *cb;
unsigned long start;
unsigned long end;
int used;
};
#define NUMBER_OF_BP 4
#define SDRAM_BANDWIDTH 400000000
#define INTMEM_BANDWIDTH 400000000
#define NBR_OF_SLOTS 64
#define NBR_OF_REGIONS 2
#define NBR_OF_CLIENTS 15
#define ARBITERS 2
#define UNASSIGNED 100
struct arbiter {
unsigned long instance;
int nbr_regions;
int nbr_clients;
int requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS];
int active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS];
};
static struct crisv32_watch_entry watches[ARBITERS][NUMBER_OF_BP] =
{
{
{regi_marb_foo_bp0},
{regi_marb_foo_bp1},
{regi_marb_foo_bp2},
{regi_marb_foo_bp3}
},
{
{regi_marb_bar_bp0},
{regi_marb_bar_bp1},
{regi_marb_bar_bp2},
{regi_marb_bar_bp3}
}
};
struct arbiter arbiters[ARBITERS] =
{
{ /* L2 cache arbiter */
.instance = regi_marb_foo,
.nbr_regions = 2,
.nbr_clients = 15
},
{ /* DDR2 arbiter */
.instance = regi_marb_bar,
.nbr_regions = 1,
.nbr_clients = 9
}
};
static int max_bandwidth[NBR_OF_REGIONS] = {SDRAM_BANDWIDTH, INTMEM_BANDWIDTH};
DEFINE_SPINLOCK(arbiter_lock);
static irqreturn_t
crisv32_foo_arbiter_irq(int irq, void *dev_id);
static irqreturn_t
crisv32_bar_arbiter_irq(int irq, void *dev_id);
/*
* "I'm the arbiter, I know the score.
* From square one I'll be watching all 64."
* (memory arbiter slots, that is)
*
* Or in other words:
* Program the memory arbiter slots for "region" according to what's
* in requested_slots[] and active_clients[], while minimizing
* latency. A caller may pass a non-zero positive amount for
* "unused_slots", which must then be the unallocated, remaining
* number of slots, free to hand out to any client.
*/
static void crisv32_arbiter_config(int arbiter, int region, int unused_slots)
{
int slot;
int client;
int interval = 0;
/*
* This vector corresponds to the hardware arbiter slots (see
* the hardware documentation for semantics). We initialize
* each slot with a suitable sentinel value outside the valid
* range {0 .. NBR_OF_CLIENTS - 1} and replace them with
* client indexes. Then it's fed to the hardware.
*/
s8 val[NBR_OF_SLOTS];
for (slot = 0; slot < NBR_OF_SLOTS; slot++)
val[slot] = -1;
for (client = 0; client < arbiters[arbiter].nbr_clients; client++) {
int pos;
/* Allocate the requested non-zero number of slots, but
* also give clients with zero-requests one slot each
* while stocks last. We do the latter here, in client
* order. This makes sure zero-request clients are the
* first to get to any spare slots, else those slots
* could, when bandwidth is allocated close to the limit,
* all be allocated to low-index non-zero-request clients
* in the default-fill loop below. Another positive but
* secondary effect is a somewhat better spread of the
* zero-bandwidth clients in the vector, avoiding some of
* the latency that could otherwise be caused by the
* partitioning of non-zero-bandwidth clients at low
* indexes and zero-bandwidth clients at high
* indexes. (Note that this spreading can only affect the
* unallocated bandwidth.) All the above only matters for
* memory-intensive situations, of course.
*/
if (!arbiters[arbiter].requested_slots[region][client]) {
/*
* Skip inactive clients. Also skip zero-slot
* allocations in this pass when there are no known
* free slots.
*/
if (!arbiters[arbiter].active_clients[region][client] ||
unused_slots <= 0)
continue;
unused_slots--;
/* Only allocate one slot for this client. */
interval = NBR_OF_SLOTS;
} else
interval = NBR_OF_SLOTS /
arbiters[arbiter].requested_slots[region][client];
pos = 0;
while (pos < NBR_OF_SLOTS) {
if (val[pos] >= 0)
pos++;
else {
val[pos] = client;
pos += interval;
}
}
}
client = 0;
for (slot = 0; slot < NBR_OF_SLOTS; slot++) {
/*
* Allocate remaining slots in round-robin
* client-number order for active clients. For this
* pass, we ignore requested bandwidth and previous
* allocations.
*/
if (val[slot] < 0) {
int first = client;
while (!arbiters[arbiter].active_clients[region][client]) {
client = (client + 1) %
arbiters[arbiter].nbr_clients;
if (client == first)
break;
}
val[slot] = client;
client = (client + 1) % arbiters[arbiter].nbr_clients;
}
if (arbiter == 0) {
if (region == EXT_REGION)
REG_WR_INT_VECT(marb_foo, regi_marb_foo,
rw_l2_slots, slot, val[slot]);
else if (region == INT_REGION)
REG_WR_INT_VECT(marb_foo, regi_marb_foo,
rw_intm_slots, slot, val[slot]);
} else {
REG_WR_INT_VECT(marb_bar, regi_marb_bar,
rw_ddr2_slots, slot, val[slot]);
}
}
}
extern char _stext[], _etext[];
static void crisv32_arbiter_init(void)
{
static int initialized;
if (initialized)
return;
initialized = 1;
/*
* CPU caches are always set to active, but with zero
* bandwidth allocated. It should be ok to allocate zero
* bandwidth for the caches, because DMA for other channels
* will supposedly finish, once their programmed amount is
* done, and then the caches will get access according to the
* "fixed scheme" for unclaimed slots. Though, if for some
* use-case somewhere, there's a maximum CPU latency for
* e.g. some interrupt, we have to start allocating specific
* bandwidth for the CPU caches too.
*/
arbiters[0].active_clients[EXT_REGION][11] = 1;
arbiters[0].active_clients[EXT_REGION][12] = 1;
crisv32_arbiter_config(0, EXT_REGION, 0);
crisv32_arbiter_config(0, INT_REGION, 0);
crisv32_arbiter_config(1, EXT_REGION, 0);
if (request_irq(MEMARB_FOO_INTR_VECT, crisv32_foo_arbiter_irq,
0, "arbiter", NULL))
printk(KERN_ERR "Couldn't allocate arbiter IRQ\n");
if (request_irq(MEMARB_BAR_INTR_VECT, crisv32_bar_arbiter_irq,
0, "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,
MARB_CLIENTS(arbiter_all_clients, arbiter_bar_all_clients),
arbiter_all_write, NULL);
#endif
/* Set up max burst sizes by default */
REG_WR_INT(marb_bar, regi_marb_bar, rw_h264_rd_burst, 3);
REG_WR_INT(marb_bar, regi_marb_bar, rw_h264_wr_burst, 3);
REG_WR_INT(marb_bar, regi_marb_bar, rw_ccd_burst, 3);
REG_WR_INT(marb_bar, regi_marb_bar, rw_vin_wr_burst, 3);
REG_WR_INT(marb_bar, regi_marb_bar, rw_vin_rd_burst, 3);
REG_WR_INT(marb_bar, regi_marb_bar, rw_sclr_rd_burst, 3);
REG_WR_INT(marb_bar, regi_marb_bar, rw_vout_burst, 3);
REG_WR_INT(marb_bar, regi_marb_bar, rw_sclr_fifo_burst, 3);
REG_WR_INT(marb_bar, regi_marb_bar, rw_l2cache_burst, 3);
}
int crisv32_arbiter_allocate_bandwidth(int client, int region,
unsigned long bandwidth)
{
int i;
int total_assigned = 0;
int total_clients = 0;
int req;
int arbiter = 0;
crisv32_arbiter_init();
if (client & 0xffff0000) {
arbiter = 1;
client >>= 16;
}
for (i = 0; i < arbiters[arbiter].nbr_clients; i++) {
total_assigned += arbiters[arbiter].requested_slots[region][i];
total_clients += arbiters[arbiter].active_clients[region][i];
}
/* Avoid division by 0 for 0-bandwidth requests. */
req = bandwidth == 0
? 0 : NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth);
/*
* We make sure that there are enough slots only for non-zero
* requests. Requesting 0 bandwidth *may* allocate slots,
* though if all bandwidth is allocated, such a client won't
* get any and will have to rely on getting memory access
* according to the fixed scheme that's the default when one
* of the slot-allocated clients doesn't claim their slot.
*/
if (total_assigned + req > NBR_OF_SLOTS)
return -ENOMEM;
arbiters[arbiter].active_clients[region][client] = 1;
arbiters[arbiter].requested_slots[region][client] = req;
crisv32_arbiter_config(arbiter, region, NBR_OF_SLOTS - total_assigned);
/* Propagate allocation from foo to bar */
if (arbiter == 0)
crisv32_arbiter_allocate_bandwidth(8 << 16,
EXT_REGION, bandwidth);
return 0;
}
/*
* Main entry for bandwidth deallocation.
*
* Strictly speaking, for a somewhat constant set of clients where
* each client gets a constant bandwidth and is just enabled or
* disabled (somewhat dynamically), no action is necessary here to
* avoid starvation for non-zero-allocation clients, as the allocated
* slots will just be unused. However, handing out those unused slots
* to active clients avoids needless latency if the "fixed scheme"
* would give unclaimed slots to an eager low-index client.
*/
void crisv32_arbiter_deallocate_bandwidth(int client, int region)
{
int i;
int total_assigned = 0;
int arbiter = 0;
if (client & 0xffff0000)
arbiter = 1;
arbiters[arbiter].requested_slots[region][client] = 0;
arbiters[arbiter].active_clients[region][client] = 0;
for (i = 0; i < arbiters[arbiter].nbr_clients; i++)
total_assigned += arbiters[arbiter].requested_slots[region][i];
crisv32_arbiter_config(arbiter, region, NBR_OF_SLOTS - total_assigned);
}
int crisv32_arbiter_watch(unsigned long start, unsigned long size,
unsigned long clients, unsigned long accesses,
watch_callback *cb)
{
int i;
int arbiter;
int used[2];
int ret = 0;
crisv32_arbiter_init();
if (start > 0x80000000) {
printk(KERN_ERR "Arbiter: %lX doesn't look like a "
"physical address", start);
return -EFAULT;
}
spin_lock(&arbiter_lock);
if (clients & 0xffff)
used[0] = 1;
if (clients & 0xffff0000)
used[1] = 1;
for (arbiter = 0; arbiter < ARBITERS; arbiter++) {
if (!used[arbiter])
continue;
for (i = 0; i < NUMBER_OF_BP; i++) {
if (!watches[arbiter][i].used) {
unsigned intr_mask;
if (arbiter)
intr_mask = REG_RD_INT(marb_bar,
regi_marb_bar, rw_intr_mask);
else
intr_mask = REG_RD_INT(marb_foo,
regi_marb_foo, rw_intr_mask);
watches[arbiter][i].used = 1;
watches[arbiter][i].start = start;
watches[arbiter][i].end = start + size;
watches[arbiter][i].cb = cb;
ret |= (i + 1) << (arbiter + 8);
if (arbiter) {
REG_WR_INT(marb_bar_bp,
watches[arbiter][i].instance,
rw_first_addr,
watches[arbiter][i].start);
REG_WR_INT(marb_bar_bp,
watches[arbiter][i].instance,
rw_last_addr,
watches[arbiter][i].end);
REG_WR_INT(marb_bar_bp,
watches[arbiter][i].instance,
rw_op, accesses);
REG_WR_INT(marb_bar_bp,
watches[arbiter][i].instance,
rw_clients,
clients & 0xffff);
} else {
REG_WR_INT(marb_foo_bp,
watches[arbiter][i].instance,
rw_first_addr,
watches[arbiter][i].start);
REG_WR_INT(marb_foo_bp,
watches[arbiter][i].instance,
rw_last_addr,
watches[arbiter][i].end);
REG_WR_INT(marb_foo_bp,
watches[arbiter][i].instance,
rw_op, accesses);
REG_WR_INT(marb_foo_bp,
watches[arbiter][i].instance,
rw_clients, clients >> 16);
}
if (i == 0)
intr_mask |= 1;
else if (i == 1)
intr_mask |= 2;
else if (i == 2)
intr_mask |= 4;
else if (i == 3)
intr_mask |= 8;
if (arbiter)
REG_WR_INT(marb_bar, regi_marb_bar,
rw_intr_mask, intr_mask);
else
REG_WR_INT(marb_foo, regi_marb_foo,
rw_intr_mask, intr_mask);
spin_unlock(&arbiter_lock);
break;
}
}
}
spin_unlock(&arbiter_lock);
if (ret)
return ret;
else
return -ENOMEM;
}
int crisv32_arbiter_unwatch(int id)
{
int arbiter;
int intr_mask;
crisv32_arbiter_init();
spin_lock(&arbiter_lock);
for (arbiter = 0; arbiter < ARBITERS; arbiter++) {
int id2;
if (arbiter)
intr_mask = REG_RD_INT(marb_bar, regi_marb_bar,
rw_intr_mask);
else
intr_mask = REG_RD_INT(marb_foo, regi_marb_foo,
rw_intr_mask);
id2 = (id & (0xff << (arbiter + 8))) >> (arbiter + 8);
if (id2 == 0)
continue;
id2--;
if ((id2 >= NUMBER_OF_BP) || (!watches[arbiter][id2].used)) {
spin_unlock(&arbiter_lock);
return -EINVAL;
}
memset(&watches[arbiter][id2], 0,
sizeof(struct crisv32_watch_entry));
if (id2 == 0)
intr_mask &= ~1;
else if (id2 == 1)
intr_mask &= ~2;
else if (id2 == 2)
intr_mask &= ~4;
else if (id2 == 3)
intr_mask &= ~8;
if (arbiter)
REG_WR_INT(marb_bar, regi_marb_bar, rw_intr_mask,
intr_mask);
else
REG_WR_INT(marb_foo, regi_marb_foo, rw_intr_mask,
intr_mask);
}
spin_unlock(&arbiter_lock);
return 0;
}
extern void show_registers(struct pt_regs *regs);
static irqreturn_t
crisv32_foo_arbiter_irq(int irq, void *dev_id)
{
reg_marb_foo_r_masked_intr masked_intr =
REG_RD(marb_foo, regi_marb_foo, r_masked_intr);
reg_marb_foo_bp_r_brk_clients r_clients;
reg_marb_foo_bp_r_brk_addr r_addr;
reg_marb_foo_bp_r_brk_op r_op;
reg_marb_foo_bp_r_brk_first_client r_first;
reg_marb_foo_bp_r_brk_size r_size;
reg_marb_foo_bp_rw_ack ack = {0};
reg_marb_foo_rw_ack_intr ack_intr = {
.bp0 = 1, .bp1 = 1, .bp2 = 1, .bp3 = 1
};
struct crisv32_watch_entry *watch;
unsigned arbiter = (unsigned)dev_id;
masked_intr = REG_RD(marb_foo, regi_marb_foo, r_masked_intr);
if (masked_intr.bp0)
watch = &watches[arbiter][0];
else if (masked_intr.bp1)
watch = &watches[arbiter][1];
else if (masked_intr.bp2)
watch = &watches[arbiter][2];
else if (masked_intr.bp3)
watch = &watches[arbiter][3];
else
return IRQ_NONE;
/* Retrieve all useful information and print it. */
r_clients = REG_RD(marb_foo_bp, watch->instance, r_brk_clients);
r_addr = REG_RD(marb_foo_bp, watch->instance, r_brk_addr);
r_op = REG_RD(marb_foo_bp, watch->instance, r_brk_op);
r_first = REG_RD(marb_foo_bp, watch->instance, r_brk_first_client);
r_size = REG_RD(marb_foo_bp, watch->instance, r_brk_size);
printk(KERN_DEBUG "Arbiter IRQ\n");
printk(KERN_DEBUG "Clients %X addr %X op %X first %X size %X\n",
REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_clients, r_clients),
REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_addr, r_addr),
REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_op, r_op),
REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_first_client, r_first),
REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_size, r_size));
REG_WR(marb_foo_bp, watch->instance, rw_ack, ack);
REG_WR(marb_foo, regi_marb_foo, rw_ack_intr, ack_intr);
printk(KERN_DEBUG "IRQ occurred at %X\n", (unsigned)get_irq_regs());
if (watch->cb)
watch->cb();
return IRQ_HANDLED;
}
static irqreturn_t
crisv32_bar_arbiter_irq(int irq, void *dev_id)
{
reg_marb_bar_r_masked_intr masked_intr =
REG_RD(marb_bar, regi_marb_bar, r_masked_intr);
reg_marb_bar_bp_r_brk_clients r_clients;
reg_marb_bar_bp_r_brk_addr r_addr;
reg_marb_bar_bp_r_brk_op r_op;
reg_marb_bar_bp_r_brk_first_client r_first;
reg_marb_bar_bp_r_brk_size r_size;
reg_marb_bar_bp_rw_ack ack = {0};
reg_marb_bar_rw_ack_intr ack_intr = {
.bp0 = 1, .bp1 = 1, .bp2 = 1, .bp3 = 1
};
struct crisv32_watch_entry *watch;
unsigned arbiter = (unsigned)dev_id;
masked_intr = REG_RD(marb_bar, regi_marb_bar, r_masked_intr);
if (masked_intr.bp0)
watch = &watches[arbiter][0];
else if (masked_intr.bp1)
watch = &watches[arbiter][1];
else if (masked_intr.bp2)
watch = &watches[arbiter][2];
else if (masked_intr.bp3)
watch = &watches[arbiter][3];
else
return IRQ_NONE;
/* Retrieve all useful information and print it. */
r_clients = REG_RD(marb_bar_bp, watch->instance, r_brk_clients);
r_addr = REG_RD(marb_bar_bp, watch->instance, r_brk_addr);
r_op = REG_RD(marb_bar_bp, watch->instance, r_brk_op);
r_first = REG_RD(marb_bar_bp, watch->instance, r_brk_first_client);
r_size = REG_RD(marb_bar_bp, watch->instance, r_brk_size);
printk(KERN_DEBUG "Arbiter IRQ\n");
printk(KERN_DEBUG "Clients %X addr %X op %X first %X size %X\n",
REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_clients, r_clients),
REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_addr, r_addr),
REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_op, r_op),
REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_first_client, r_first),
REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_size, r_size));
REG_WR(marb_bar_bp, watch->instance, rw_ack, ack);
REG_WR(marb_bar, regi_marb_bar, rw_ack_intr, ack_intr);
printk(KERN_DEBUG "IRQ occurred at %X\n", (unsigned)get_irq_regs()->erp);
if (watch->cb)
watch->cb();
return IRQ_HANDLED;
}

View File

@ -1,184 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/* Wrapper for DMA channel allocator that starts clocks etc */
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <mach/dma.h>
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/marb_defs.h>
#include <hwregs/clkgen_defs.h>
#include <hwregs/strmux_defs.h>
#include <linux/errno.h>
#include <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_clkgen_rw_clk_ctrl clk_ctrl;
reg_strmux_rw_cfg strmux_cfg;
if (crisv32_arbiter_allocate_bandwidth(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(KERN_ERR "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(clkgen, regi_clkgen, rw_clk_ctrl);
strmux_cfg = REG_RD(strmux, regi_strmux, rw_cfg);
switch (dmanr) {
case 0:
case 1:
clk_ctrl.dma0_1_eth = 1;
break;
case 2:
case 3:
clk_ctrl.dma2_3_strcop = 1;
break;
case 4:
case 5:
clk_ctrl.dma4_5_iop = 1;
break;
case 6:
case 7:
clk_ctrl.sser_ser_dma6_7 = 1;
break;
case 9:
case 11:
clk_ctrl.dma9_11 = 1;
break;
#if MAX_DMA_CHANNELS-1 != 11
#error Check dma.c
#endif
default:
spin_unlock_irqrestore(&dma_lock, flags);
if (options & DMA_VERBOSE_ON_ERROR)
printk(KERN_ERR "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_eth:
if (dmanr == 0)
strmux_cfg.dma0 = regk_strmux_eth;
else if (dmanr == 1)
strmux_cfg.dma1 = regk_strmux_eth;
else
panic("Invalid DMA channel for eth\n");
break;
case dma_ser0:
if (dmanr == 0)
strmux_cfg.dma0 = regk_strmux_ser0;
else if (dmanr == 1)
strmux_cfg.dma1 = regk_strmux_ser0;
else
panic("Invalid DMA channel for ser0\n");
break;
case dma_ser3:
if (dmanr == 2)
strmux_cfg.dma2 = regk_strmux_ser3;
else if (dmanr == 3)
strmux_cfg.dma3 = regk_strmux_ser3;
else
panic("Invalid DMA channel for ser3\n");
break;
case dma_strp:
if (dmanr == 2)
strmux_cfg.dma2 = regk_strmux_strcop;
else if (dmanr == 3)
strmux_cfg.dma3 = regk_strmux_strcop;
else
panic("Invalid DMA channel for strp\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_iop:
if (dmanr == 4)
strmux_cfg.dma4 = regk_strmux_iop;
else if (dmanr == 5)
strmux_cfg.dma5 = regk_strmux_iop;
else
panic("Invalid DMA channel for iop\n");
break;
case dma_ser2:
if (dmanr == 6)
strmux_cfg.dma6 = regk_strmux_ser2;
else if (dmanr == 7)
strmux_cfg.dma7 = regk_strmux_ser2;
else
panic("Invalid DMA channel for ser2\n");
break;
case dma_sser:
if (dmanr == 6)
strmux_cfg.dma6 = regk_strmux_sser;
else if (dmanr == 7)
strmux_cfg.dma7 = regk_strmux_sser;
else
panic("Invalid DMA channel for sser\n");
break;
case dma_ser4:
if (dmanr == 9)
strmux_cfg.dma9 = regk_strmux_ser4;
else
panic("Invalid DMA channel for ser4\n");
break;
case dma_jpeg:
if (dmanr == 9)
strmux_cfg.dma9 = regk_strmux_jpeg;
else
panic("Invalid DMA channel for JPEG\n");
break;
case dma_h264:
if (dmanr == 11)
strmux_cfg.dma11 = regk_strmux_h264;
else
panic("Invalid DMA channel for H264\n");
break;
}
used_dma_channels[dmanr] = 1;
used_dma_channels_users[dmanr] = device_id;
REG_WR(clkgen, regi_clkgen, 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

@ -1,119 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* DDR 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 decompressor to the kernel
*
* Copyright (C) 2005-2007 Axis Communications AB
*
* Authors: Mikael Starvik <starvik@axis.com>
*/
/* Just to be certain the config file is included, we include it here
* explicitly instead of depending on it being included in the file that
* uses this code.
*/
#include <hwregs/asm/reg_map_asm.h>
#include <hwregs/asm/ddr2_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 ddr2 MDS for initialization sequence
; 2. Wait 200us
move.d 10000, $r2
1: bne 1b
subq 1, $r2
; Start clock
move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_phy_cfg), $r0
move.d REG_STATE(ddr2, rw_phy_cfg, en, yes), $r1
move.d $r1, [$r0]
; 2. Wait 200us
move.d 10000, $r2
1: bne 1b
subq 1, $r2
; Reset phy and start calibration
move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_phy_ctrl), $r0
move.d REG_STATE(ddr2, rw_phy_ctrl, rst, yes) | \
REG_STATE(ddr2, rw_phy_ctrl, cal_rst, yes), $r1
move.d $r1, [$r0]
move.d REG_STATE(ddr2, rw_phy_ctrl, cal_start, yes), $r1
move.d $r1, [$r0]
; 2. Wait 200us
move.d 10000, $r2
1: bne 1b
subq 1, $r2
; Issue commands
move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_ctrl), $r0
move.d sdram_commands_start, $r2
command_loop:
movu.b [$r2+], $r1
movu.w [$r2+], $r3
do_cmd:
lslq 16, $r1
or.d $r3, $r1
move.d $r1, [$r0]
; 2. Wait 200us
move.d 10000, $r4
1: bne 1b
subq 1, $r4
cmp.d sdram_commands_end, $r2
blo command_loop
nop
; Set timing
move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_timing), $r0
move.d CONFIG_ETRAX_DDR2_TIMING, $r1
move.d $r1, [$r0]
; Set latency
move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_latency), $r0
move.d CONFIG_ETRAX_DDR2_LATENCY, $r1
move.d $r1, [$r0]
; Set configuration
move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_cfg), $r0
move.d CONFIG_ETRAX_DDR2_CONFIG, $r1
move.d $r1, [$r0]
ba after_sdram_commands
nop
sdram_commands_start:
.byte regk_ddr2_deselect
.word 0
.byte regk_ddr2_pre
.word regk_ddr2_pre_all
.byte regk_ddr2_emrs2
.word 0
.byte regk_ddr2_emrs3
.word 0
.byte regk_ddr2_emrs
.word regk_ddr2_dll_en
.byte regk_ddr2_mrs
.word regk_ddr2_dll_rst
.byte regk_ddr2_pre
.word regk_ddr2_pre_all
.byte regk_ddr2_ref
.word 0
.byte regk_ddr2_ref
.word 0
.byte regk_ddr2_mrs
.word CONFIG_ETRAX_DDR2_MRS & 0xffff
.byte regk_ddr2_emrs
.word regk_ddr2_ocd_default | regk_ddr2_dll_en
.byte regk_ddr2_emrs
.word regk_ddr2_ocd_exit | regk_ddr2_dll_en | (CONFIG_ETRAX_DDR2_MRS >> 16)
sdram_commands_end:
.align 1
after_sdram_commands:

View File

@ -1,54 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* 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-2007 Axis Communications AB
*
* Authors: Mikael Starvik <starvik@axis.com>
*/
#include <hwregs/asm/reg_map_asm.h>
#include <hwregs/asm/ddr2_defs_asm.h>
#include <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(ddr2, regi_ddr2_ctrl, rw_cfg)
.dword CONFIG_ETRAX_DDR2_CONFIG
.dword REG_ADDR(ddr2, regi_ddr2_ctrl, rw_latency)
.dword CONFIG_ETRAX_DDR2_LATENCY
.dword REG_ADDR(ddr2, regi_ddr2_ctrl, rw_timing)
.dword CONFIG_ETRAX_DDR2_TIMING
.dword CONFIG_ETRAX_DDR2_MRS
.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 0 ; No more register values

View File

@ -1,389 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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) 2005-2007 Axis Communications AB.
*/
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <pinmux.h>
#include <hwregs/pinmux_defs.h>
#include <hwregs/clkgen_defs.h>
#undef DEBUG
#define PINS 80
#define PORT_PINS 32
#define PORTS 3
static char pins[PINS];
static DEFINE_SPINLOCK(pinmux_lock);
static void crisv32_pinmux_set(int port);
int
crisv32_pinmux_init(void)
{
static int initialized;
if (!initialized) {
initialized = 1;
REG_WR_INT(pinmux, regi_pinmux, rw_hwprot, 0);
crisv32_pinmux_alloc(PORT_A, 0, 31, pinmux_gpio);
crisv32_pinmux_alloc(PORT_B, 0, 31, pinmux_gpio);
crisv32_pinmux_alloc(PORT_C, 0, 15, 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 * PORT_PINS + i] != pinmux_none) &&
(pins[port * PORT_PINS + i] != pinmux_gpio) &&
(pins[port * PORT_PINS + 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 * PORT_PINS + 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;
reg_pinmux_rw_hwprot hwprot;
reg_clkgen_rw_clk_ctrl clk_ctrl;
spin_lock_irqsave(&pinmux_lock, flags);
/* Save internal data for recovery */
memcpy(saved, pins, sizeof pins);
crisv32_pinmux_init(); /* must be done before we read rw_hwprot */
hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl);
switch (function) {
case pinmux_eth:
clk_ctrl.eth = regk_clkgen_yes;
clk_ctrl.dma0_1_eth = regk_clkgen_yes;
ret = crisv32_pinmux_alloc(PORT_B, 8, 23, pinmux_fixed);
ret |= crisv32_pinmux_alloc(PORT_B, 24, 25, pinmux_fixed);
hwprot.eth = hwprot.eth_mdio = regk_pinmux_yes;
break;
case pinmux_geth:
ret = crisv32_pinmux_alloc(PORT_B, 0, 7, pinmux_fixed);
hwprot.geth = regk_pinmux_yes;
break;
case pinmux_tg_cmos:
clk_ctrl.ccd_tg_100 = clk_ctrl.ccd_tg_200 = regk_clkgen_yes;
ret = crisv32_pinmux_alloc(PORT_B, 27, 29, pinmux_fixed);
hwprot.tg_clk = regk_pinmux_yes;
break;
case pinmux_tg_ccd:
clk_ctrl.ccd_tg_100 = clk_ctrl.ccd_tg_200 = regk_clkgen_yes;
ret = crisv32_pinmux_alloc(PORT_B, 27, 31, pinmux_fixed);
ret |= crisv32_pinmux_alloc(PORT_C, 0, 15, pinmux_fixed);
hwprot.tg = hwprot.tg_clk = regk_pinmux_yes;
break;
case pinmux_vout:
clk_ctrl.strdma0_2_video = regk_clkgen_yes;
ret = crisv32_pinmux_alloc(PORT_A, 8, 18, pinmux_fixed);
hwprot.vout = hwprot.vout_sync = regk_pinmux_yes;
break;
case pinmux_ser1:
clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes;
ret = crisv32_pinmux_alloc(PORT_A, 24, 25, pinmux_fixed);
hwprot.ser1 = regk_pinmux_yes;
break;
case pinmux_ser2:
clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes;
ret = crisv32_pinmux_alloc(PORT_A, 26, 27, pinmux_fixed);
hwprot.ser2 = regk_pinmux_yes;
break;
case pinmux_ser3:
clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes;
ret = crisv32_pinmux_alloc(PORT_A, 28, 29, pinmux_fixed);
hwprot.ser3 = regk_pinmux_yes;
break;
case pinmux_ser4:
clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes;
ret = crisv32_pinmux_alloc(PORT_A, 30, 31, pinmux_fixed);
hwprot.ser4 = regk_pinmux_yes;
break;
case pinmux_sser:
clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes;
ret = crisv32_pinmux_alloc(PORT_A, 19, 23, pinmux_fixed);
hwprot.sser = regk_pinmux_yes;
break;
case pinmux_pio:
hwprot.pio = regk_pinmux_yes;
ret = 0;
break;
case pinmux_pwm0:
ret = crisv32_pinmux_alloc(PORT_A, 30, 30, pinmux_fixed);
hwprot.pwm0 = regk_pinmux_yes;
break;
case pinmux_pwm1:
ret = crisv32_pinmux_alloc(PORT_A, 31, 31, pinmux_fixed);
hwprot.pwm1 = regk_pinmux_yes;
break;
case pinmux_pwm2:
ret = crisv32_pinmux_alloc(PORT_B, 26, 26, pinmux_fixed);
hwprot.pwm2 = regk_pinmux_yes;
break;
case pinmux_i2c0:
ret = crisv32_pinmux_alloc(PORT_A, 0, 1, pinmux_fixed);
hwprot.i2c0 = regk_pinmux_yes;
break;
case pinmux_i2c1:
ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed);
hwprot.i2c1 = regk_pinmux_yes;
break;
case pinmux_i2c1_3wire:
ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed);
ret |= crisv32_pinmux_alloc(PORT_A, 7, 7, pinmux_fixed);
hwprot.i2c1 = hwprot.i2c1_sen = regk_pinmux_yes;
break;
case pinmux_i2c1_sda1:
ret = crisv32_pinmux_alloc(PORT_A, 2, 4, pinmux_fixed);
hwprot.i2c1 = hwprot.i2c1_sda1 = regk_pinmux_yes;
break;
case pinmux_i2c1_sda2:
ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed);
ret |= crisv32_pinmux_alloc(PORT_A, 5, 5, pinmux_fixed);
hwprot.i2c1 = hwprot.i2c1_sda2 = regk_pinmux_yes;
break;
case pinmux_i2c1_sda3:
ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed);
ret |= crisv32_pinmux_alloc(PORT_A, 6, 6, pinmux_fixed);
hwprot.i2c1 = hwprot.i2c1_sda3 = regk_pinmux_yes;
break;
default:
ret = -EINVAL;
break;
}
if (!ret) {
REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot);
REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl);
} 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;
int pin = port * PORT_PINS;
for (i = 0; (i < PORT_PINS) && (pin < PINS); i++, pin++) {
if (pins[pin] == pinmux_gpio)
gpio_val |= (1 << i);
else if (pins[pin] == pinmux_iop)
iop_val |= (1 << i);
}
REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_gio_pa + 4 * port,
gpio_val);
REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_iop_pa + 4 * 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 || port < 0)
return -EINVAL;
spin_lock_irqsave(&pinmux_lock, flags);
for (i = first_pin; i <= last_pin; i++)
pins[port * PORT_PINS + i] = pinmux_none;
crisv32_pinmux_set(port);
spin_unlock_irqrestore(&pinmux_lock, flags);
return 0;
}
int
crisv32_pinmux_dealloc_fixed(enum fixed_function function)
{
int ret = -EINVAL;
char saved[sizeof pins];
unsigned long flags;
reg_pinmux_rw_hwprot hwprot;
spin_lock_irqsave(&pinmux_lock, flags);
/* Save internal data for recovery */
memcpy(saved, pins, sizeof pins);
crisv32_pinmux_init(); /* must be done before we read rw_hwprot */
hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
switch (function) {
case pinmux_eth:
ret = crisv32_pinmux_dealloc(PORT_B, 8, 23);
ret |= crisv32_pinmux_dealloc(PORT_B, 24, 25);
ret |= crisv32_pinmux_dealloc(PORT_B, 0, 7);
hwprot.eth = hwprot.eth_mdio = hwprot.geth = regk_pinmux_no;
break;
case pinmux_tg_cmos:
ret = crisv32_pinmux_dealloc(PORT_B, 27, 29);
hwprot.tg_clk = regk_pinmux_no;
break;
case pinmux_tg_ccd:
ret = crisv32_pinmux_dealloc(PORT_B, 27, 31);
ret |= crisv32_pinmux_dealloc(PORT_C, 0, 15);
hwprot.tg = hwprot.tg_clk = regk_pinmux_no;
break;
case pinmux_vout:
ret = crisv32_pinmux_dealloc(PORT_A, 8, 18);
hwprot.vout = hwprot.vout_sync = regk_pinmux_no;
break;
case pinmux_ser1:
ret = crisv32_pinmux_dealloc(PORT_A, 24, 25);
hwprot.ser1 = regk_pinmux_no;
break;
case pinmux_ser2:
ret = crisv32_pinmux_dealloc(PORT_A, 26, 27);
hwprot.ser2 = regk_pinmux_no;
break;
case pinmux_ser3:
ret = crisv32_pinmux_dealloc(PORT_A, 28, 29);
hwprot.ser3 = regk_pinmux_no;
break;
case pinmux_ser4:
ret = crisv32_pinmux_dealloc(PORT_A, 30, 31);
hwprot.ser4 = regk_pinmux_no;
break;
case pinmux_sser:
ret = crisv32_pinmux_dealloc(PORT_A, 19, 23);
hwprot.sser = regk_pinmux_no;
break;
case pinmux_pwm0:
ret = crisv32_pinmux_dealloc(PORT_A, 30, 30);
hwprot.pwm0 = regk_pinmux_no;
break;
case pinmux_pwm1:
ret = crisv32_pinmux_dealloc(PORT_A, 31, 31);
hwprot.pwm1 = regk_pinmux_no;
break;
case pinmux_pwm2:
ret = crisv32_pinmux_dealloc(PORT_B, 26, 26);
hwprot.pwm2 = regk_pinmux_no;
break;
case pinmux_i2c0:
ret = crisv32_pinmux_dealloc(PORT_A, 0, 1);
hwprot.i2c0 = regk_pinmux_no;
break;
case pinmux_i2c1:
ret = crisv32_pinmux_dealloc(PORT_A, 2, 3);
hwprot.i2c1 = regk_pinmux_no;
break;
case pinmux_i2c1_3wire:
ret = crisv32_pinmux_dealloc(PORT_A, 2, 3);
ret |= crisv32_pinmux_dealloc(PORT_A, 7, 7);
hwprot.i2c1 = hwprot.i2c1_sen = regk_pinmux_no;
break;
case pinmux_i2c1_sda1:
ret = crisv32_pinmux_dealloc(PORT_A, 2, 4);
hwprot.i2c1_sda1 = regk_pinmux_no;
break;
case pinmux_i2c1_sda2:
ret = crisv32_pinmux_dealloc(PORT_A, 2, 3);
ret |= crisv32_pinmux_dealloc(PORT_A, 5, 5);
hwprot.i2c1_sda2 = regk_pinmux_no;
break;
case pinmux_i2c1_sda3:
ret = crisv32_pinmux_dealloc(PORT_A, 2, 3);
ret |= crisv32_pinmux_dealloc(PORT_A, 6, 6);
hwprot.i2c1_sda3 = regk_pinmux_no;
break;
default:
ret = -EINVAL;
break;
}
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_dump(void)
{
int i, j;
int pin = 0;
crisv32_pinmux_init();
for (i = 0; i < PORTS; i++) {
pin++;
printk(KERN_DEBUG "Port %c\n", 'A'+i);
for (j = 0; (j < PORT_PINS) && (pin < PINS); j++, pin++)
printk(KERN_DEBUG
" Pin %d = %d\n", j, pins[i * PORT_PINS + j]);
}
}
__initcall(crisv32_pinmux_init);

View File

@ -1,198 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
if ETRAXFS
menu "ETRAX FS options"
depends on ETRAXFS
config ETRAX_DRAM_VIRTUAL_BASE
hex
depends on ETRAX_ARCH_V32
default "c0000000"
config ETRAX_SERIAL_PORTS
int
default 4
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 default 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.
endmenu
endif

View File

@ -1,8 +0,0 @@
#
# Makefile for the linux kernel.
#
obj-y := dma.o pinmux.o arbiter.o
clean:

View File

@ -1,405 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Memory arbiter functions. Allocates bandwidth through the
* arbiter and sets up arbiter breakpoints.
*
* The algorithm first assigns slots to the clients that has specified
* bandwidth (e.g. ethernet) and then the remaining slots are divided
* on all the active clients.
*
* Copyright (c) 2004-2007 Axis Communications AB.
*/
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/marb_defs.h>
#include <arbiter.h>
#include <hwregs/intr_vect.h>
#include <linux/interrupt.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <asm/irq_regs.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 u8 requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS];
static u8 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);
/*
* "I'm the arbiter, I know the score.
* From square one I'll be watching all 64."
* (memory arbiter slots, that is)
*
* Or in other words:
* Program the memory arbiter slots for "region" according to what's
* in requested_slots[] and active_clients[], while minimizing
* latency. A caller may pass a non-zero positive amount for
* "unused_slots", which must then be the unallocated, remaining
* number of slots, free to hand out to any client.
*/
static void crisv32_arbiter_config(int region, int unused_slots)
{
int slot;
int client;
int interval = 0;
/*
* This vector corresponds to the hardware arbiter slots (see
* the hardware documentation for semantics). We initialize
* each slot with a suitable sentinel value outside the valid
* range {0 .. NBR_OF_CLIENTS - 1} and replace them with
* client indexes. Then it's fed to the hardware.
*/
s8 val[NBR_OF_SLOTS];
for (slot = 0; slot < NBR_OF_SLOTS; slot++)
val[slot] = -1;
for (client = 0; client < NBR_OF_CLIENTS; client++) {
int pos;
/* Allocate the requested non-zero number of slots, but
* also give clients with zero-requests one slot each
* while stocks last. We do the latter here, in client
* order. This makes sure zero-request clients are the
* first to get to any spare slots, else those slots
* could, when bandwidth is allocated close to the limit,
* all be allocated to low-index non-zero-request clients
* in the default-fill loop below. Another positive but
* secondary effect is a somewhat better spread of the
* zero-bandwidth clients in the vector, avoiding some of
* the latency that could otherwise be caused by the
* partitioning of non-zero-bandwidth clients at low
* indexes and zero-bandwidth clients at high
* indexes. (Note that this spreading can only affect the
* unallocated bandwidth.) All the above only matters for
* memory-intensive situations, of course.
*/
if (!requested_slots[region][client]) {
/*
* Skip inactive clients. Also skip zero-slot
* allocations in this pass when there are no known
* free slots.
*/
if (!active_clients[region][client]
|| unused_slots <= 0)
continue;
unused_slots--;
/* Only allocate one slot for this client. */
interval = NBR_OF_SLOTS;
} else
interval =
NBR_OF_SLOTS / requested_slots[region][client];
pos = 0;
while (pos < NBR_OF_SLOTS) {
if (val[pos] >= 0)
pos++;
else {
val[pos] = client;
pos += interval;
}
}
}
client = 0;
for (slot = 0; slot < NBR_OF_SLOTS; slot++) {
/*
* Allocate remaining slots in round-robin
* client-number order for active clients. For this
* pass, we ignore requested bandwidth and previous
* allocations.
*/
if (val[slot] < 0) {
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;
if (initialized)
return;
initialized = 1;
/*
* CPU caches are always set to active, but with zero
* bandwidth allocated. It should be ok to allocate zero
* bandwidth for the caches, because DMA for other channels
* will supposedly finish, once their programmed amount is
* done, and then the caches will get access according to the
* "fixed scheme" for unclaimed slots. Though, if for some
* use-case somewhere, there's a maximum CPU latency for
* e.g. some interrupt, we have to start allocating specific
* bandwidth for the CPU caches too.
*/
active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1;
crisv32_arbiter_config(EXT_REGION, 0);
crisv32_arbiter_config(INT_REGION, 0);
if (request_irq(MEMARB_INTR_VECT, crisv32_arbiter_irq, 0,
"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
}
/* Main entry for bandwidth allocation. */
int crisv32_arbiter_allocate_bandwidth(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];
}
/* Avoid division by 0 for 0-bandwidth requests. */
req = bandwidth == 0
? 0 : NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth);
/*
* We make sure that there are enough slots only for non-zero
* requests. Requesting 0 bandwidth *may* allocate slots,
* though if all bandwidth is allocated, such a client won't
* get any and will have to rely on getting memory access
* according to the fixed scheme that's the default when one
* of the slot-allocated clients doesn't claim their slot.
*/
if (total_assigned + req > NBR_OF_SLOTS)
return -ENOMEM;
active_clients[region][client] = 1;
requested_slots[region][client] = req;
crisv32_arbiter_config(region, NBR_OF_SLOTS - total_assigned);
return 0;
}
/*
* Main entry for bandwidth deallocation.
*
* Strictly speaking, for a somewhat constant set of clients where
* each client gets a constant bandwidth and is just enabled or
* disabled (somewhat dynamically), no action is necessary here to
* avoid starvation for non-zero-allocation clients, as the allocated
* slots will just be unused. However, handing out those unused slots
* to active clients avoids needless latency if the "fixed scheme"
* would give unclaimed slots to an eager low-index client.
*/
void crisv32_arbiter_deallocate_bandwidth(int client, int region)
{
int i;
int total_assigned = 0;
requested_slots[region][client] = 0;
active_clients[region][client] = 0;
for (i = 0; i < NBR_OF_CLIENTS; i++)
total_assigned += requested_slots[region][i];
crisv32_arbiter_config(region, NBR_OF_SLOTS - total_assigned);
}
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(KERN_ERR "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.bp1 = 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)
{
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(KERN_INFO "Arbiter IRQ\n");
printk(KERN_INFO "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(KERN_INFO "IRQ occurred at %lX\n", get_irq_regs()->erp);
if (watch->cb)
watch->cb();
return IRQ_HANDLED;
}

View File

@ -1,229 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/* Wrapper for DMA channel allocator that starts clocks etc */
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <asm/dma.h>
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/marb_defs.h>
#include <hwregs/config_defs.h>
#include <hwregs/strmux_defs.h>
#include <linux/errno.h>
#include <mach/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_bandwidth(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(KERN_ERR "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(KERN_ERR "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);
}

Some files were not shown because too many files have changed in this diff Show More