mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 10:00:51 +07:00
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:
parent
bb9d812643
commit
c690eddc2f
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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>;
|
||||
};
|
||||
|
||||
|
10
MAINTAINERS
10
MAINTAINERS
@ -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>
|
||||
|
@ -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"
|
@ -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
|
@ -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
|
@ -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
|
@ -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.
|
||||
|
||||
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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);
|
@ -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);
|
@ -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);
|
||||
|
@ -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 ********************************/
|
@ -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
@ -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:
|
||||
|
@ -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);
|
@ -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);
|
@ -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);
|
@ -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
|
||||
|
@ -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);
|
@ -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
@ -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
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
@ -1,6 +0,0 @@
|
||||
#
|
||||
# Makefile for Etrax-specific library files..
|
||||
#
|
||||
|
||||
lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
@ -1,6 +0,0 @@
|
||||
#
|
||||
# Makefile for the linux cris-specific parts of the memory manager.
|
||||
#
|
||||
|
||||
obj-y := fault.o init.o tlb.o
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
/* At the time of this writing, there's no equivalent ld option. */
|
||||
OUTPUT_ARCH (cris)
|
@ -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
|
@ -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
|
@ -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/
|
@ -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
@ -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);
|
@ -1,5 +0,0 @@
|
||||
#
|
||||
# Makefile for Etrax-specific drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o
|
@ -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;
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
#
|
||||
# Makefile for Etrax-specific drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o
|
@ -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;
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
#
|
||||
# Makefile for Etrax cardbus driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ETRAX_CARDBUS) += bios.o
|
@ -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
@ -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:
|
||||
|
@ -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);
|
@ -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
|
||||
|
@ -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);
|
@ -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;
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
@ -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
|
@ -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
@ -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
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
@ -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);
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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);
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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);
|
@ -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
|
@ -1,8 +0,0 @@
|
||||
#
|
||||
# Makefile for the linux kernel.
|
||||
#
|
||||
|
||||
obj-y := dma.o pinmux.o arbiter.o
|
||||
|
||||
clean:
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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:
|
@ -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
|
@ -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);
|
@ -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
|
@ -1,8 +0,0 @@
|
||||
#
|
||||
# Makefile for the linux kernel.
|
||||
#
|
||||
|
||||
obj-y := dma.o pinmux.o arbiter.o
|
||||
|
||||
clean:
|
||||
|
@ -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;
|
||||
}
|
@ -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
Loading…
Reference in New Issue
Block a user