M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
+398
View File
@@ -0,0 +1,398 @@
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
+244
View File
@@ -0,0 +1,244 @@
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.
+632
View File
@@ -0,0 +1,632 @@
if ETRAX_ARCH_V10
config ETRAX_ETHERNET
bool "Ethernet support"
depends on ETRAX_ARCH_V10
select ETHERNET
select NET_CORE
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_USB_HOST_PORT1
bool "USB port 1 enabled"
depends on ETRAX_USB_HOST
default n
config ETRAX_USB_HOST_PORT2
bool "USB port 2 enabled"
depends on ETRAX_USB_HOST
default n
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_BUTTON_BITMASK
hex "PA-buttons bitmask"
depends on ETRAX_GPIO
default "02"
help
This is a bitmask with information about what bits on PA that
are used for buttons.
Most products has a so called TEST button on PA1, if that's true
use 02 here.
Use 00 if there are no buttons on PA.
If the bitmask is <> 00 a button driver will be included in the gpio
driver. ETRAX general I/O support must be enabled.
config ETRAX_PA_CHANGEABLE_DIR
hex "PA user changeable dir mask"
depends on ETRAX_GPIO
default "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.
config ETRAX_DS1302_RST_ON_GENERIC_PORT
bool "DS1302 RST on Generic Port"
depends on ETRAX_DS1302
help
If your product has the RST signal line for the DS1302 RTC on the
Generic Port then say Y here, otherwise leave it as N in which
case the RST signal line is assumed to be connected to Port PB
(just like the SCL and SDA lines).
config ETRAX_DS1302_RSTBIT
int "DS1302 RST bit number"
depends on ETRAX_DS1302
default "2"
help
This is the bit number for the RST signal line of the DS1302 RTC on
the selected port. If you have selected the generic port then it
should be bit 27, otherwise your best bet is bit 5.
config ETRAX_DS1302_SCLBIT
int "DS1302 SCL bit number"
depends on ETRAX_DS1302
default "1"
help
This is the bit number for the SCL signal line of the DS1302 RTC on
Port PB. This is probably best left at 3.
config ETRAX_DS1302_SDABIT
int "DS1302 SDA bit number"
depends on ETRAX_DS1302
default "0"
help
This is the bit number for the SDA signal line of the DS1302 RTC on
Port PB. This is probably best left at 2.
config ETRAX_DS1302_TRICKLE_CHARGE
int "DS1302 Trickle charger value"
depends on ETRAX_DS1302
default "0"
help
This controls the initial value of the trickle charge register.
0 = disabled (use this if you are unsure or have a non rechargeable battery)
Otherwise the following values can be OR:ed together to control the
charge current:
1 = 2kohm, 2 = 4kohm, 3 = 4kohm
4 = 1 diode, 8 = 2 diodes
Allowed values are (increasing current): 0, 11, 10, 9, 7, 6, 5
endif
@@ -0,0 +1,12 @@
#
# 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_DS1302) += ds1302.o
obj-$(CONFIG_ETRAX_PCF8563) += pcf8563.o
obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o
@@ -0,0 +1,432 @@
/*
* 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
},
};
#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
/* Main flash device */
static struct mtd_partition main_partition = {
.name = "main",
.size = 0,
.offset = 0
};
#endif
/*
* 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 boundries.
*
* The only known restriction to how you can mount your chips is that each
* chip select must hold similar flash chips. But you need external hardware
* to do that anyway and you can put totally different chips on cse0 and cse1
* so it isn't really much of a restriction.
*/
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++;
}
#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
if (mymtd) {
main_partition.size = mymtd->size;
err = mtd_device_register(mymtd, &main_partition, 1);
if (err)
panic("axisflashmap: Could not initialize "
"partition for whole main mtd device!\n");
}
#endif
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) || (CONFIG_MTDRAM_ABS_POS != 0)
/* No use trying to boot this kernel from RAM. Panic! */
printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM "
"device due to kernel (mis)configuration!\n");
panic("This kernel cannot boot from RAM!\n");
#else
struct mtd_info *mtd_ram;
mtd_ram = 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);
+515
View File
@@ -0,0 +1,515 @@
/*!***************************************************************************
*!
*! FILE NAME : ds1302.c
*!
*! DESCRIPTION: Implements an interface for the DS1302 RTC through Etrax I/O
*!
*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init
*!
*! ---------------------------------------------------------------------------
*!
*! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN
*!
*!***************************************************************************/
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/bcd.h>
#include <linux/capability.h>
#include <asm/uaccess.h>
#include <arch/svinto.h>
#include <asm/io.h>
#include <asm/rtc.h>
#include <arch/io_interface_mux.h>
#include "i2c.h"
#define RTC_MAJOR_NR 121 /* local major, change later */
static DEFINE_MUTEX(ds1302_mutex);
static const char ds1302_name[] = "ds1302";
/* The DS1302 might be connected to different bits on different products.
* It has three signals - SDA, SCL and RST. RST and SCL are always outputs,
* but SDA can have a selected direction.
* For now, only PORT_PB is hardcoded.
*/
/* The RST bit may be on either the Generic Port or Port PB. */
#ifdef CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT
#define TK_RST_OUT(x) REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, CONFIG_ETRAX_DS1302_RSTBIT, x)
#define TK_RST_DIR(x)
#else
#define TK_RST_OUT(x) REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, CONFIG_ETRAX_DS1302_RSTBIT, x)
#define TK_RST_DIR(x) REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, CONFIG_ETRAX_DS1302_RSTBIT, x)
#endif
#define TK_SDA_OUT(x) REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, CONFIG_ETRAX_DS1302_SDABIT, x)
#define TK_SCL_OUT(x) REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, CONFIG_ETRAX_DS1302_SCLBIT, x)
#define TK_SDA_IN() ((*R_PORT_PB_READ >> CONFIG_ETRAX_DS1302_SDABIT) & 1)
/* 1 is out, 0 is in */
#define TK_SDA_DIR(x) REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, CONFIG_ETRAX_DS1302_SDABIT, x)
#define TK_SCL_DIR(x) REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, CONFIG_ETRAX_DS1302_SCLBIT, x)
/*
* The reason for tempudelay and not udelay is that loops_per_usec
* (used in udelay) is not set when functions here are called from time.c
*/
static void tempudelay(int usecs)
{
volatile int loops;
for(loops = usecs * 12; loops > 0; loops--)
/* nothing */;
}
/* Send 8 bits. */
static void
out_byte(unsigned char x)
{
int i;
TK_SDA_DIR(1);
for (i = 8; i--;) {
/* The chip latches incoming bits on the rising edge of SCL. */
TK_SCL_OUT(0);
TK_SDA_OUT(x & 1);
tempudelay(1);
TK_SCL_OUT(1);
tempudelay(1);
x >>= 1;
}
TK_SDA_DIR(0);
}
static unsigned char
in_byte(void)
{
unsigned char x = 0;
int i;
/* Read byte. Bits come LSB first, on the falling edge of SCL.
* Assume SDA is in input direction already.
*/
TK_SDA_DIR(0);
for (i = 8; i--;) {
TK_SCL_OUT(0);
tempudelay(1);
x >>= 1;
x |= (TK_SDA_IN() << 7);
TK_SCL_OUT(1);
tempudelay(1);
}
return x;
}
/* Prepares for a transaction by de-activating RST (active-low). */
static void
start(void)
{
TK_SCL_OUT(0);
tempudelay(1);
TK_RST_OUT(0);
tempudelay(5);
TK_RST_OUT(1);
}
/* Ends a transaction by taking RST active again. */
static void
stop(void)
{
tempudelay(2);
TK_RST_OUT(0);
}
/* Enable writing. */
static void
ds1302_wenable(void)
{
start();
out_byte(0x8e); /* Write control register */
out_byte(0x00); /* Disable write protect bit 7 = 0 */
stop();
}
/* Disable writing. */
static void
ds1302_wdisable(void)
{
start();
out_byte(0x8e); /* Write control register */
out_byte(0x80); /* Disable write protect bit 7 = 0 */
stop();
}
/* Read a byte from the selected register in the DS1302. */
unsigned char
ds1302_readreg(int reg)
{
unsigned char x;
start();
out_byte(0x81 | (reg << 1)); /* read register */
x = in_byte();
stop();
return x;
}
/* Write a byte to the selected register. */
void
ds1302_writereg(int reg, unsigned char val)
{
#ifndef CONFIG_ETRAX_RTC_READONLY
int do_writereg = 1;
#else
int do_writereg = 0;
if (reg == RTC_TRICKLECHARGER)
do_writereg = 1;
#endif
if (do_writereg) {
ds1302_wenable();
start();
out_byte(0x80 | (reg << 1)); /* write register */
out_byte(val);
stop();
ds1302_wdisable();
}
}
void
get_rtc_time(struct rtc_time *rtc_tm)
{
unsigned long flags;
local_irq_save(flags);
rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
rtc_tm->tm_hour = CMOS_READ(RTC_HOURS);
rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
rtc_tm->tm_mon = CMOS_READ(RTC_MONTH);
rtc_tm->tm_year = CMOS_READ(RTC_YEAR);
local_irq_restore(flags);
rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
/*
* Account for differences between how the RTC uses the values
* and how they are defined in a struct rtc_time;
*/
if (rtc_tm->tm_year <= 69)
rtc_tm->tm_year += 100;
rtc_tm->tm_mon--;
}
static unsigned char days_in_mo[] =
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/* ioctl that supports RTC_RD_TIME and RTC_SET_TIME (read and set time/date). */
static int rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned long flags;
switch(cmd) {
case RTC_RD_TIME: /* read the time/date from RTC */
{
struct rtc_time rtc_tm;
memset(&rtc_tm, 0, sizeof (struct rtc_time));
get_rtc_time(&rtc_tm);
if (copy_to_user((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time)))
return -EFAULT;
return 0;
}
case RTC_SET_TIME: /* set the RTC */
{
struct rtc_time rtc_tm;
unsigned char mon, day, hrs, min, sec, leap_yr;
unsigned int yrs;
if (!capable(CAP_SYS_TIME))
return -EPERM;
if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time)))
return -EFAULT;
yrs = rtc_tm.tm_year + 1900;
mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */
day = rtc_tm.tm_mday;
hrs = rtc_tm.tm_hour;
min = rtc_tm.tm_min;
sec = rtc_tm.tm_sec;
if ((yrs < 1970) || (yrs > 2069))
return -EINVAL;
leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
if ((mon > 12) || (day == 0))
return -EINVAL;
if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
return -EINVAL;
if ((hrs >= 24) || (min >= 60) || (sec >= 60))
return -EINVAL;
if (yrs >= 2000)
yrs -= 2000; /* RTC (0, 1, ... 69) */
else
yrs -= 1900; /* RTC (70, 71, ... 99) */
sec = bin2bcd(sec);
min = bin2bcd(min);
hrs = bin2bcd(hrs);
day = bin2bcd(day);
mon = bin2bcd(mon);
yrs = bin2bcd(yrs);
local_irq_save(flags);
CMOS_WRITE(yrs, RTC_YEAR);
CMOS_WRITE(mon, RTC_MONTH);
CMOS_WRITE(day, RTC_DAY_OF_MONTH);
CMOS_WRITE(hrs, RTC_HOURS);
CMOS_WRITE(min, RTC_MINUTES);
CMOS_WRITE(sec, RTC_SECONDS);
local_irq_restore(flags);
/* Notice that at this point, the RTC is updated but
* the kernel is still running with the old time.
* You need to set that separately with settimeofday
* or adjtimex.
*/
return 0;
}
case RTC_SET_CHARGE: /* set the RTC TRICKLE CHARGE register */
{
int tcs_val;
if (!capable(CAP_SYS_TIME))
return -EPERM;
if(copy_from_user(&tcs_val, (int*)arg, sizeof(int)))
return -EFAULT;
tcs_val = RTC_TCR_PATTERN | (tcs_val & 0x0F);
ds1302_writereg(RTC_TRICKLECHARGER, tcs_val);
return 0;
}
case RTC_VL_READ:
{
/* TODO:
* Implement voltage low detection support
*/
printk(KERN_WARNING "DS1302: RTC Voltage Low detection"
" is not supported\n");
return 0;
}
case RTC_VL_CLR:
{
/* TODO:
* Nothing to do since Voltage Low detection is not supported
*/
return 0;
}
default:
return -ENOIOCTLCMD;
}
}
static long rtc_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret;
mutex_lock(&ds1302_mutex);
ret = rtc_ioctl(file, cmd, arg);
mutex_unlock(&ds1302_mutex);
return ret;
}
static void
print_rtc_status(void)
{
struct rtc_time tm;
get_rtc_time(&tm);
/*
* There is no way to tell if the luser has the RTC set for local
* time or for Universal Standard Time (GMT). Probably local though.
*/
printk(KERN_INFO "rtc_time\t: %02d:%02d:%02d\n",
tm.tm_hour, tm.tm_min, tm.tm_sec);
printk(KERN_INFO "rtc_date\t: %04d-%02d-%02d\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
}
/* The various file operations we support. */
static const struct file_operations rtc_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = rtc_unlocked_ioctl,
.llseek = noop_llseek,
};
/* Probe for the chip by writing something to its RAM and try reading it back. */
#define MAGIC_PATTERN 0x42
static int __init
ds1302_probe(void)
{
int retval, res;
TK_RST_DIR(1);
TK_SCL_DIR(1);
TK_SDA_DIR(0);
/* Try to talk to timekeeper. */
ds1302_wenable();
start();
out_byte(0xc0); /* write RAM byte 0 */
out_byte(MAGIC_PATTERN); /* write something magic */
start();
out_byte(0xc1); /* read RAM byte 0 */
if((res = in_byte()) == MAGIC_PATTERN) {
stop();
ds1302_wdisable();
printk(KERN_INFO "%s: RTC found.\n", ds1302_name);
printk(KERN_INFO "%s: SDA, SCL, RST on PB%i, PB%i, %s%i\n",
ds1302_name,
CONFIG_ETRAX_DS1302_SDABIT,
CONFIG_ETRAX_DS1302_SCLBIT,
#ifdef CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT
"GENIO",
#else
"PB",
#endif
CONFIG_ETRAX_DS1302_RSTBIT);
print_rtc_status();
retval = 1;
} else {
stop();
retval = 0;
}
return retval;
}
/* Just probe for the RTC and register the device to handle the ioctl needed. */
int __init
ds1302_init(void)
{
#ifdef CONFIG_ETRAX_I2C
i2c_init();
#endif
if (!ds1302_probe()) {
#ifdef CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT
#if CONFIG_ETRAX_DS1302_RSTBIT == 27
/*
* The only way to set g27 to output is to enable ATA.
*
* Make sure that R_GEN_CONFIG is setup correct.
*/
/* Allocating the ATA interface will grab almost all
* pins in I/O groups a, b, c and d. A consequence of
* allocating the ATA interface is that the fixed
* interfaces shared RAM, parallel port 0, parallel
* port 1, parallel port W, SCSI-8 port 0, SCSI-8 port
* 1, SCSI-W, serial port 2, serial port 3,
* synchronous serial port 3 and USB port 2 and almost
* all GPIO pins on port g cannot be used.
*/
if (cris_request_io_interface(if_ata, "ds1302/ATA")) {
printk(KERN_WARNING "ds1302: Failed to get IO interface\n");
return -1;
}
#elif CONFIG_ETRAX_DS1302_RSTBIT == 0
if (cris_io_interface_allocate_pins(if_gpio_grp_a,
'g',
CONFIG_ETRAX_DS1302_RSTBIT,
CONFIG_ETRAX_DS1302_RSTBIT)) {
printk(KERN_WARNING "ds1302: Failed to get IO interface\n");
return -1;
}
/* Set the direction of this bit to out. */
genconfig_shadow = ((genconfig_shadow &
~IO_MASK(R_GEN_CONFIG, g0dir)) |
(IO_STATE(R_GEN_CONFIG, g0dir, out)));
*R_GEN_CONFIG = genconfig_shadow;
#endif
if (!ds1302_probe()) {
printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name);
return -1;
}
#else
printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name);
return -1;
#endif
}
/* Initialise trickle charger */
ds1302_writereg(RTC_TRICKLECHARGER,
RTC_TCR_PATTERN |(CONFIG_ETRAX_DS1302_TRICKLE_CHARGE & 0x0F));
/* Start clock by resetting CLOCK_HALT */
ds1302_writereg(RTC_SECONDS, (ds1302_readreg(RTC_SECONDS) & 0x7F));
return 0;
}
static int __init ds1302_register(void)
{
ds1302_init();
if (register_chrdev(RTC_MAJOR_NR, ds1302_name, &rtc_fops)) {
printk(KERN_INFO "%s: unable to get major %d for rtc\n",
ds1302_name, RTC_MAJOR_NR);
return -1;
}
return 0;
}
module_init(ds1302_register);
+852
View File
@@ -0,0 +1,852 @@
/*!*****************************************************************************
*!
*! 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 <asm/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 begning 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 */
}
}
module_init(eeprom_init);
+856
View File
@@ -0,0 +1,856 @@
/*
* 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 unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait);
/* private data per open() of this driver */
struct gpio_private {
struct gpio_private *next;
/* 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 unsigned int gpio_poll(struct file *file, poll_table *wait)
{
unsigned int 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 = POLLIN|POLLRDNORM;
}
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().
*/
unsigned long inline 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 */
unsigned long inline 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 cpu_idle() in kernel/process.c
* The check in cpu_idle() reduces latency from ~15 ms to ~6 ms
* in some tests.
*/
res = request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt,
IRQF_SHARED | IRQF_DISABLED, "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 | IRQF_DISABLED, "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);
+698
View File
@@ -0,0 +1,698 @@
/*!***************************************************************************
*!
*! 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 ********************************/
+17
View File
@@ -0,0 +1,17 @@
/* 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);
+380
View File
@@ -0,0 +1,380 @@
/*
* PCF8563 RTC
*
* From Phillips' datasheet:
*
* The PCF8563 is a CMOS real-time clock/calendar optimized for low power
* consumption. A programmable clock output, interrupt output and voltage
* low detector are also provided. All address and data are transferred
* serially via two-line bidirectional I2C-bus. Maximum bus speed is
* 400 kbits/s. The built-in word address register is incremented
* automatically after each written or read byte.
*
* Copyright (c) 2002-2007, Axis Communications AB
* All rights reserved.
*
* Author: Tobias Anderberg <tobiasa@axis.com>.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/bcd.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/rtc.h>
#include "i2c.h"
#define PCF8563_MAJOR 121 /* Local major number. */
#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */
#define PCF8563_NAME "PCF8563"
#define DRIVER_VERSION "$Revision: 1.24 $"
/* I2C bus slave registers. */
#define RTC_I2C_READ 0xa3
#define RTC_I2C_WRITE 0xa2
/* Two simple wrapper macros, saves a few keystrokes. */
#define rtc_read(x) i2c_readreg(RTC_I2C_READ, x)
#define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y)
static DEFINE_MUTEX(pcf8563_mutex);
static DEFINE_MUTEX(rtc_lock); /* Protect state etc */
static const unsigned char days_in_month[] =
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static long pcf8563_unlocked_ioctl(struct file *, unsigned int, unsigned long);
/* Cache VL bit value read at driver init since writing the RTC_SECOND
* register clears the VL status.
*/
static int voltage_low;
static const struct file_operations pcf8563_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = pcf8563_unlocked_ioctl,
.llseek = noop_llseek,
};
unsigned char
pcf8563_readreg(int reg)
{
unsigned char res = rtc_read(reg);
/* The PCF8563 does not return 0 for unimplemented bits. */
switch (reg) {
case RTC_SECONDS:
case RTC_MINUTES:
res &= 0x7F;
break;
case RTC_HOURS:
case RTC_DAY_OF_MONTH:
res &= 0x3F;
break;
case RTC_WEEKDAY:
res &= 0x07;
break;
case RTC_MONTH:
res &= 0x1F;
break;
case RTC_CONTROL1:
res &= 0xA8;
break;
case RTC_CONTROL2:
res &= 0x1F;
break;
case RTC_CLOCKOUT_FREQ:
case RTC_TIMER_CONTROL:
res &= 0x83;
break;
}
return res;
}
void
pcf8563_writereg(int reg, unsigned char val)
{
rtc_write(reg, val);
}
void
get_rtc_time(struct rtc_time *tm)
{
tm->tm_sec = rtc_read(RTC_SECONDS);
tm->tm_min = rtc_read(RTC_MINUTES);
tm->tm_hour = rtc_read(RTC_HOURS);
tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH);
tm->tm_wday = rtc_read(RTC_WEEKDAY);
tm->tm_mon = rtc_read(RTC_MONTH);
tm->tm_year = rtc_read(RTC_YEAR);
if (tm->tm_sec & 0x80) {
printk(KERN_ERR "%s: RTC Voltage Low - reliable date/time "
"information is no longer guaranteed!\n", PCF8563_NAME);
}
tm->tm_year = bcd2bin(tm->tm_year) +
((tm->tm_mon & 0x80) ? 100 : 0);
tm->tm_sec &= 0x7F;
tm->tm_min &= 0x7F;
tm->tm_hour &= 0x3F;
tm->tm_mday &= 0x3F;
tm->tm_wday &= 0x07; /* Not coded in BCD. */
tm->tm_mon &= 0x1F;
tm->tm_sec = bcd2bin(tm->tm_sec);
tm->tm_min = bcd2bin(tm->tm_min);
tm->tm_hour = bcd2bin(tm->tm_hour);
tm->tm_mday = bcd2bin(tm->tm_mday);
tm->tm_mon = bcd2bin(tm->tm_mon);
tm->tm_mon--; /* Month is 1..12 in RTC but 0..11 in linux */
}
int __init
pcf8563_init(void)
{
static int res;
static int first = 1;
if (!first)
return res;
first = 0;
/* Initiate the i2c protocol. */
res = i2c_init();
if (res < 0) {
printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n");
return res;
}
/*
* First of all we need to reset the chip. This is done by
* clearing control1, control2 and clk freq and resetting
* all alarms.
*/
if (rtc_write(RTC_CONTROL1, 0x00) < 0)
goto err;
if (rtc_write(RTC_CONTROL2, 0x00) < 0)
goto err;
if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0)
goto err;
if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0)
goto err;
/* Reset the alarms. */
if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0)
goto err;
if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0)
goto err;
if (rtc_write(RTC_DAY_ALARM, 0x80) < 0)
goto err;
if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0)
goto err;
/* Check for low voltage, and warn about it. */
if (rtc_read(RTC_SECONDS) & 0x80) {
voltage_low = 1;
printk(KERN_WARNING "%s: RTC Voltage Low - reliable "
"date/time information is no longer guaranteed!\n",
PCF8563_NAME);
}
return res;
err:
printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME);
res = -1;
return res;
}
void __exit
pcf8563_exit(void)
{
unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME);
}
/*
* ioctl calls for this driver. Why return -ENOTTY upon error? Because
* POSIX says so!
*/
static int pcf8563_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
/* Some sanity checks. */
if (_IOC_TYPE(cmd) != RTC_MAGIC)
return -ENOTTY;
if (_IOC_NR(cmd) > RTC_MAX_IOCTL)
return -ENOTTY;
switch (cmd) {
case RTC_RD_TIME:
{
struct rtc_time tm;
mutex_lock(&rtc_lock);
memset(&tm, 0, sizeof tm);
get_rtc_time(&tm);
if (copy_to_user((struct rtc_time *) arg, &tm,
sizeof tm)) {
mutex_unlock(&rtc_lock);
return -EFAULT;
}
mutex_unlock(&rtc_lock);
return 0;
}
case RTC_SET_TIME:
{
int leap;
int year;
int century;
struct rtc_time tm;
memset(&tm, 0, sizeof tm);
if (!capable(CAP_SYS_TIME))
return -EPERM;
if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm))
return -EFAULT;
/* Convert from struct tm to struct rtc_time. */
tm.tm_year += 1900;
tm.tm_mon += 1;
/*
* Check if tm.tm_year is a leap year. A year is a leap
* year if it is divisible by 4 but not 100, except
* that years divisible by 400 _are_ leap years.
*/
year = tm.tm_year;
leap = (tm.tm_mon == 2) &&
((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
/* Perform some sanity checks. */
if ((tm.tm_year < 1970) ||
(tm.tm_mon > 12) ||
(tm.tm_mday == 0) ||
(tm.tm_mday > days_in_month[tm.tm_mon] + leap) ||
(tm.tm_wday >= 7) ||
(tm.tm_hour >= 24) ||
(tm.tm_min >= 60) ||
(tm.tm_sec >= 60))
return -EINVAL;
century = (tm.tm_year >= 2000) ? 0x80 : 0;
tm.tm_year = tm.tm_year % 100;
tm.tm_year = bin2bcd(tm.tm_year);
tm.tm_mon = bin2bcd(tm.tm_mon);
tm.tm_mday = bin2bcd(tm.tm_mday);
tm.tm_hour = bin2bcd(tm.tm_hour);
tm.tm_min = bin2bcd(tm.tm_min);
tm.tm_sec = bin2bcd(tm.tm_sec);
tm.tm_mon |= century;
mutex_lock(&rtc_lock);
rtc_write(RTC_YEAR, tm.tm_year);
rtc_write(RTC_MONTH, tm.tm_mon);
rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */
rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday);
rtc_write(RTC_HOURS, tm.tm_hour);
rtc_write(RTC_MINUTES, tm.tm_min);
rtc_write(RTC_SECONDS, tm.tm_sec);
mutex_unlock(&rtc_lock);
return 0;
}
case RTC_VL_READ:
if (voltage_low) {
printk(KERN_ERR "%s: RTC Voltage Low - "
"reliable date/time information is no "
"longer guaranteed!\n", PCF8563_NAME);
}
if (copy_to_user((int *) arg, &voltage_low, sizeof(int)))
return -EFAULT;
return 0;
case RTC_VL_CLR:
{
/* Clear the VL bit in the seconds register in case
* the time has not been set already (which would
* have cleared it). This does not really matter
* because of the cached voltage_low value but do it
* anyway for consistency. */
int ret = rtc_read(RTC_SECONDS);
rtc_write(RTC_SECONDS, (ret & 0x7F));
/* Clear the cached value. */
voltage_low = 0;
return 0;
}
default:
return -ENOTTY;
}
return 0;
}
static long pcf8563_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
mutex_lock(&pcf8563_mutex);
ret = pcf8563_ioctl(filp, cmd, arg);
mutex_unlock(&pcf8563_mutex);
return ret;
}
static int __init pcf8563_register(void)
{
if (pcf8563_init() < 0) {
printk(KERN_INFO "%s: Unable to initialize Real-Time Clock "
"Driver, %s\n", PCF8563_NAME, DRIVER_VERSION);
return -1;
}
if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) {
printk(KERN_INFO "%s: Unable to get major number %d for RTC device.\n",
PCF8563_NAME, PCF8563_MAJOR);
return -1;
}
printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME,
DRIVER_VERSION);
/* Check for low voltage, and warn about it. */
if (voltage_low) {
printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
"information is no longer guaranteed!\n", PCF8563_NAME);
}
return 0;
}
module_init(pcf8563_register);
module_exit(pcf8563_exit);
File diff suppressed because it is too large Load Diff
+18
View File
@@ -0,0 +1,18 @@
# $Id: Makefile,v 1.6 2004/12/13 12:21:51 starvik Exp $
#
# 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:
@@ -0,0 +1,16 @@
#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);
@@ -0,0 +1,566 @@
/* 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>
#include <asm/io.h> /* Get SIMCOUT. */
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;
#ifdef CONFIG_SVINTO_SIM
/* no use to simulate the serial debug output */
SIMCOUT(buf, len);
return;
#endif
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 sercons = {
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 sercons0 = {
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 sercons1 = {
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 sercons2 = {
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 sercons3 = {
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(&sercons);
register_console(&sercons0);
register_console(&sercons1);
register_console(&sercons2);
register_console(&sercons3);
init_dummy_console();
return 0;
}
first = 0;
register_console(&sercons);
start_port(port);
#ifdef CONFIG_ETRAX_KGDB
start_port(kgdb_port);
#endif
return 0;
}
__initcall(init_etrax_debug);
+287
View File
@@ -0,0 +1,287 @@
/* 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);
+952
View File
@@ -0,0 +1,952 @@
/*
* 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 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_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) && !defined(CONFIG_SVINTO_SIM)
;; Check if we're waiting for reset to happen, as signalled by
;; hard_reset_now setting cause_of_death to a magic value. If so, just
;; get stuck until reset happens.
.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 and not CONFIG_SVINTO_SIM */
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.
.global kernel_execve
kernel_execve:
move.d __NR_execve, $r9
break 13
ret
nop
.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 /* 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 */
/*
* NOTE!! This doesn't have to be exact - we just have
* to make sure we have _enough_ of the "sys_ni_syscall"
* entries. Don't panic if you notice that this hasn't
* been shrunk every time we add a new system call.
*/
.rept NR_syscalls-(.-sys_call_table)/4
.long sys_ni_syscall
.endr
@@ -0,0 +1,878 @@
/*
* 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 <asm/rtc.h>
#include <arch/svinto.h>
#include <asm/fasttimer.h>
#include <linux/proc_fs.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
static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len
,int *eof, void *data_unused);
static struct proc_dir_entry *fasttimer_proc_entry;
#endif /* CONFIG_PROC_FS */
#ifdef CONFIG_PROC_FS
/* This value is very much based on testing */
#define BIG_BUF_SIZE (500 + NUM_TIMER_STATS * 300)
static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len
,int *eof, void *data_unused)
{
unsigned long flags;
int i = 0;
int num_to_show;
struct fasttime_t tv;
struct fast_timer *t, *nextt;
static char *bigbuf = NULL;
static unsigned long used;
if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE)))
{
used = 0;
if (buf)
buf[0] = '\0';
return 0;
}
if (!offset || !used)
{
do_gettimeofday_fast(&tv);
used = 0;
used += sprintf(bigbuf + used, "Fast timers added: %i\n",
fast_timers_added);
used += sprintf(bigbuf + used, "Fast timers started: %i\n",
fast_timers_started);
used += sprintf(bigbuf + used, "Fast timer interrupts: %i\n",
fast_timer_ints);
used += sprintf(bigbuf + used, "Fast timers expired: %i\n",
fast_timers_expired);
used += sprintf(bigbuf + used, "Fast timers deleted: %i\n",
fast_timers_deleted);
used += sprintf(bigbuf + used, "Fast timer running: %s\n",
fast_timer_running ? "yes" : "no");
used += sprintf(bigbuf + used, "Current time: %lu.%06lu\n",
(unsigned long)tv.tv_jiff,
(unsigned long)tv.tv_usec);
#ifdef FAST_TIMER_SANITY_CHECKS
used += sprintf(bigbuf + used, "Sanity failed: %i\n",
sanity_failed);
#endif
used += sprintf(bigbuf + used, "\n");
#ifdef DEBUG_LOG_INCLUDED
{
int end_i = debug_log_cnt;
i = 0;
if (debug_log_cnt_wrapped)
{
i = debug_log_cnt;
}
while ((i != end_i || (debug_log_cnt_wrapped && !used)) &&
used+100 < BIG_BUF_SIZE)
{
used += sprintf(bigbuf + used, debug_log_string[i],
debug_log_value[i]);
i = (i+1) % DEBUG_LOG_MAX;
}
}
used += sprintf(bigbuf + used, "\n");
#endif
num_to_show = (fast_timers_started < NUM_TIMER_STATS ? fast_timers_started:
NUM_TIMER_STATS);
used += sprintf(bigbuf + used, "Timers started: %i\n", fast_timers_started);
for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE) ; i++)
{
int cur = (fast_timers_started - i - 1) % NUM_TIMER_STATS;
#if 1 //ndef FAST_TIMER_LOG
used += sprintf(bigbuf + used, "div: %i 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];
used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
"d: %6li us data: 0x%08lX"
"\n",
t->name,
(unsigned long)t->tv_set.tv_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
);
#endif
}
used += sprintf(bigbuf + used, "\n");
#ifdef FAST_TIMER_LOG
num_to_show = (fast_timers_added < NUM_TIMER_STATS ? fast_timers_added:
NUM_TIMER_STATS);
used += sprintf(bigbuf + used, "Timers added: %i\n", fast_timers_added);
for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE); i++)
{
t = &timer_added_log[(fast_timers_added - i - 1) % NUM_TIMER_STATS];
used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
"d: %6li us data: 0x%08lX"
"\n",
t->name,
(unsigned long)t->tv_set.tv_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
);
}
used += sprintf(bigbuf + used, "\n");
num_to_show = (fast_timers_expired < NUM_TIMER_STATS ? fast_timers_expired:
NUM_TIMER_STATS);
used += sprintf(bigbuf + used, "Timers expired: %i\n", fast_timers_expired);
for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE); i++)
{
t = &timer_expired_log[(fast_timers_expired - i - 1) % NUM_TIMER_STATS];
used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
"d: %6li us data: 0x%08lX"
"\n",
t->name,
(unsigned long)t->tv_set.tv_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
);
}
used += sprintf(bigbuf + used, "\n");
#endif
used += sprintf(bigbuf + used, "Active timers:\n");
local_irq_save(flags);
t = fast_timer_list;
while (t != NULL && (used+100 < BIG_BUF_SIZE))
{
nextt = t->next;
local_irq_restore(flags);
used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
"d: %6li us data: 0x%08lX"
/* " func: 0x%08lX" */
"\n",
t->name,
(unsigned long)t->tv_set.tv_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
/* , t->function */
);
local_irq_save(flags);
if (t->next != nextt)
{
printk(KERN_WARNING "timer removed!\n");
}
t = nextt;
}
local_irq_restore(flags);
}
if (used - offset < len)
{
len = used - offset;
}
memcpy(buf, bigbuf + offset, len);
*start = buf;
*eof = 1;
return len;
}
#endif /* PROC_FS */
#ifdef FAST_TIMER_TEST
static volatile unsigned long i = 0;
static volatile int num_test_timeout = 0;
static struct fast_timer tr[10];
static int exp_num[10];
static struct 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
if ((fasttimer_proc_entry = create_proc_entry( "fasttimer", 0, 0 )))
fasttimer_proc_entry->read_proc = proc_fasttimer_read;
#endif /* PROC_FS */
if(request_irq(TIMER1_IRQ_NBR, timer1_handler, 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);
+730
View File
@@ -0,0 +1,730 @@
/*
* Head of the kernel - alter with care
*
* Copyright (C) 2000, 2001 Axis Communications AB
*
* Authors: Bjorn Wesen (bjornw@axis.com)
*
*/
#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
.text
;; This is the entry point of the kernel. We are in supervisor mode.
;; 0x00000000 if Flash, 0x40004000 if DRAM
;; since etrax actually starts at address 2 when booting from flash, we
;; put a nop (2 bytes) here first so we dont accidentally skip the di
;;
;; NOTICE! The 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.
.section ".init.text", "ax"
_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.
#ifndef CONFIG_SVINTO_SIM
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]
#endif
;; 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
#ifdef CONFIG_BLK_DEV_ETRAXIDE
;; disable ATA before enabling it in genconfig below
moveq 0,$r0
move.d $r0,[R_ATA_CTRL_DATA]
move.d $r0,[R_ATA_TRANSFER_CNT]
move.d $r0,[R_ATA_CONFIG]
#if 0
move.d R_PORT_G_DATA, $r1
move.d $r0, [$r1]; assert ATA bus-reset
nop
nop
nop
nop
nop
nop
move.d 0x08000000,$r0
move.d $r0,[$r1]
#endif
#endif
#ifdef CONFIG_JULIETTE
;; configure external DMA channel 0 before enabling it in genconfig
moveq 0,$r0
move.d $r0,[R_EXT_DMA_0_ADDR]
; cnt enable, word size, output, stop, size 0
move.d IO_STATE (R_EXT_DMA_0_CMD, cnt, enable) \
| IO_STATE (R_EXT_DMA_0_CMD, rqpol, ahigh) \
| IO_STATE (R_EXT_DMA_0_CMD, apol, ahigh) \
| IO_STATE (R_EXT_DMA_0_CMD, rq_ack, burst) \
| IO_STATE (R_EXT_DMA_0_CMD, wid, word) \
| IO_STATE (R_EXT_DMA_0_CMD, dir, output) \
| IO_STATE (R_EXT_DMA_0_CMD, run, stop) \
| IO_FIELD (R_EXT_DMA_0_CMD, trf_count, 0),$r0
move.d $r0,[R_EXT_DMA_0_CMD]
;; reset dma4 and wait for completion
moveq IO_STATE (R_DMA_CH4_CMD, cmd, reset),$r0
move.b $r0,[R_DMA_CH4_CMD]
1: move.b [R_DMA_CH4_CMD],$r0
and.b IO_MASK (R_DMA_CH4_CMD, cmd),$r0
cmp.b IO_STATE (R_DMA_CH4_CMD, cmd, reset),$r0
beq 1b
nop
;; reset dma5 and wait for completion
moveq IO_STATE (R_DMA_CH5_CMD, cmd, reset),$r0
move.b $r0,[R_DMA_CH5_CMD]
1: move.b [R_DMA_CH5_CMD],$r0
and.b IO_MASK (R_DMA_CH5_CMD, cmd),$r0
cmp.b IO_STATE (R_DMA_CH5_CMD, cmd, reset),$r0
beq 1b
nop
#endif
;; 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
#if defined(CONFIG_ETRAX_DEF_R_PORT_G0_DIR_OUT)
or.d IO_STATE (R_GEN_CONFIG, g0dir, out),$r0
#endif
#if defined(CONFIG_ETRAX_DEF_R_PORT_G8_15_DIR_OUT)
or.d IO_STATE (R_GEN_CONFIG, g8_15dir, out),$r0
#endif
#if defined(CONFIG_ETRAX_DEF_R_PORT_G16_23_DIR_OUT)
or.d IO_STATE (R_GEN_CONFIG, g16_23dir, out),$r0
#endif
#if defined(CONFIG_ETRAX_DEF_R_PORT_G24_DIR_OUT)
or.d IO_STATE (R_GEN_CONFIG, g24dir, out),$r0
#endif
move.d $r0,[genconfig_shadow] ; init a shadow register of R_GEN_CONFIG
#ifndef CONFIG_SVINTO_SIM
move.d $r0,[R_GEN_CONFIG]
#if 0
moveq 4,$r0
move.b $r0,[R_DMA_CH6_CMD] ; reset (ser0 dma out)
move.b $r0,[R_DMA_CH7_CMD] ; reset (ser0 dma in)
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
#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_PA7)
or.b IO_STATE (R_PORT_PA_DIR, dir7, output),$r0
#endif
move.b $r0,[port_pa_dir_shadow]
move.b $r0,[R_PORT_PA_DIR]
move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA,$r0
#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_PA7)
#if defined(CONFIG_BLUETOOTH_RESET_ACTIVE_HIGH)
and.b ~(1 << 7),$r0
#else
or.b (1 << 7),$r0
#endif
#endif
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
#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_PB5)
or.b IO_STATE (R_PORT_PB_DIR, dir5, output),$r0
#endif
move.b $r0,[port_pb_dir_shadow]
move.b $r0,[R_PORT_PB_DIR]
move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA,$r0
#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_PB5)
#if defined(CONFIG_BLUETOOTH_RESET_ACTIVE_HIGH)
and.b ~(1 << 5),$r0
#else
or.b (1 << 5),$r0
#endif
#endif
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
#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_G10)
#if defined(CONFIG_BLUETOOTH_RESET_ACTIVE_HIGH)
and.d ~(1 << 10),$r0
#else
or.d (1 << 10),$r0
#endif
#endif
#if defined(CONFIG_BLUETOOTH) && defined(CONFIG_BLUETOOTH_RESET_G11)
#if defined(CONFIG_BLUETOOTH_RESET_ACTIVE_HIGH)
and.d ~(1 << 11),$r0
#else
or.d (1 << 11),$r0
#endif
#endif
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
#endif /* CONFIG_SVINTO_SIM */
jump start_kernel ; jump into the C-function start_kernel in init/main.c
.data
etrax_irv:
.dword 0
romfs_start:
.dword 0
romfs_length:
.dword 0
romfs_in_flash:
.dword 0
;; put some special pages at the beginning of the kernel aligned
;; to page boundaries - the kernel cannot start until after this
#ifdef CONFIG_CRIS_LOW_MAP
swapper_pg_dir = 0x60002000
#else
swapper_pg_dir = 0xc0002000
#endif
.section ".init.data", "aw"
#include "../lib/hw_settings.S"
File diff suppressed because it is too large Load Diff
+245
View File
@@ -0,0 +1,245 @@
/*
* 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 */
#ifndef CONFIG_SVINTO_SIM
*R_IRQ_MASK0_CLR = 0xffffffff;
*R_IRQ_MASK1_CLR = 0xffffffff;
*R_IRQ_MASK2_CLR = 0xffffffff;
#endif
*R_VECT_MASK_CLR = 0xffffffff;
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
+272
View File
@@ -0,0 +1,272 @@
/*
* 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/slab.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <arch/svinto.h>
#include <linux/init.h>
#include <arch/system.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
}
/*
* Free current thread data structures etc..
*/
void exit_thread(void)
{
/* Nothing needs to be done. */
}
/* 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) && !defined(CONFIG_SVINTO_SIM)
extern int cause_of_death;
#endif
printk("*** HARD RESET ***\n");
local_irq_disable();
#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
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! */ ;
}
/*
* Return saved PC of a blocked thread.
*/
unsigned long thread_saved_pc(struct task_struct *t)
{
return task_pt_regs(t)->irp;
}
static void kernel_thread_helper(void* dummy, int (*fn)(void *), void * arg)
{
fn(arg);
do_exit(-1); /* Should never be called, return bad exit value */
}
/*
* Create a kernel thread
*/
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
struct pt_regs regs;
memset(&regs, 0, sizeof(regs));
/* Don't use r10 since that is set to 0 in copy_thread */
regs.r11 = (unsigned long)fn;
regs.r12 = (unsigned long)arg;
regs.irp = (unsigned long)kernel_thread_helper;
regs.dccr = 1 << I_DCCR_BITNR;
/* Ok, create the new process.. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}
/* 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);
int copy_thread(unsigned long clone_flags, unsigned long usp,
unsigned long unused,
struct task_struct *p, struct pt_regs *regs)
{
struct pt_regs * childregs;
struct switch_stack *swstack;
/* put the pt_regs structure at the end of the new kernel stack page and fix it up
* remember that the task_struct doubles as the kernel stack for the task
*/
childregs = task_pt_regs(p);
*childregs = *regs; /* struct copy of pt_regs */
p->set_child_tid = p->clear_child_tid = NULL;
childregs->r10 = 0; /* child returns 0 after a fork/clone */
/* put the switch stack right below the pt_regs */
swstack = ((struct switch_stack *)childregs) - 1;
swstack->r9 = 0; /* parameter to ret_from_sys_call, 0 == dont restart the syscall */
/* we want to return into ret_from_sys_call after the _resume */
swstack->return_ip = (unsigned long) ret_from_fork; /* Will call ret_from_sys_call */
/* fix the user-mode stackpointer */
p->thread.usp = usp;
/* 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;
}
/*
* Be aware of the "magic" 7th argument in the four system-calls below.
* They need the latest stackframe, which is put as the 7th argument by
* entry.S. The previous arguments are dummies or actually used, but need
* to be defined to reach the 7th argument.
*
* N.B.: Another method to get the stackframe is to use current_regs(). But
* it returns the latest stack-frame stacked when going from _user mode_ and
* some of these (at least sys_clone) are called from kernel-mode sometimes
* (for example during kernel_thread, above) and thus cannot use it. Thus,
* to be sure not to get any surprises, we use the method for the other calls
* as well.
*/
asmlinkage int sys_fork(long r10, long r11, long r12, long r13, long mof, long srp,
struct pt_regs *regs)
{
return do_fork(SIGCHLD, rdusp(), regs, 0, NULL, NULL);
}
/* if newusp is 0, we just grab the old usp */
/* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */
asmlinkage int sys_clone(unsigned long newusp, unsigned long flags,
int* parent_tid, int* child_tid, long mof, long srp,
struct pt_regs *regs)
{
if (!newusp)
newusp = rdusp();
return do_fork(flags, newusp, regs, 0, parent_tid, child_tid);
}
/* vfork is a system call in i386 because of register-pressure - maybe
* we can remove it and handle it in libc but we put it here until then.
*/
asmlinkage int sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp,
struct pt_regs *regs)
{
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL);
}
/*
* sys_execve() executes a new program.
*/
asmlinkage int sys_execve(const char *fname,
const char *const *argv,
const char *const *envp,
long r13, long mof, long srp,
struct pt_regs *regs)
{
int error;
char *filename;
filename = getname(fname);
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename, argv, envp, regs);
putname(filename);
out:
return error;
}
unsigned long get_wchan(struct task_struct *p)
{
#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();
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);
}
+202
View File
@@ -0,0 +1,202 @@
/*
* Copyright (C) 2000-2003, Axis Communications AB.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/signal.h>
#include <linux/security.h>
#include <asm/uaccess.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/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;
}
}
+104
View File
@@ -0,0 +1,104 @@
/*
*
* 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];
return 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);
}
#endif /* CONFIG_PROC_FS */
void
show_etrax_copyright(void)
{
printk(KERN_INFO
"Linux/CRIS port on ETRAX 100LX (c) 2001 Axis Communications AB\n");
}
@@ -0,0 +1,36 @@
/*
* 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;
+552
View File
@@ -0,0 +1,552 @@
/*
* 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/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 <asm/uaccess.h>
#include <arch/system.h>
#define DEBUG_SIG 0
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
/* a syscall in Linux/CRIS is a break 13 instruction which is 2 bytes */
/* manipulate regs so that upon return, it will be re-executed */
/* 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);
/*
* Atomically swap in the new signal mask, and wait for a signal. Define
* dummy arguments to be able to reach the regs argument. (Note that this
* arrangement relies on old_sigset_t occupying one register.)
*/
int sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof,
long srp, struct pt_regs *regs)
{
mask &= _BLOCKABLE;
spin_lock_irq(&current->sighand->siglock);
current->saved_sigmask = current->blocked;
siginitset(&current->blocked, mask);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
current->state = TASK_INTERRUPTIBLE;
schedule();
set_thread_flag(TIF_RESTORE_SIGMASK);
return -ERESTARTNOHAND;
}
int sys_sigaction(int sig, const struct old_sigaction __user *act,
struct old_sigaction *oact)
{
struct k_sigaction new_ka, old_ka;
int ret;
if (act) {
old_sigset_t mask;
if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
__get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
__get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
return -EFAULT;
__get_user(new_ka.sa.sa_flags, &act->sa_flags);
__get_user(mask, &act->sa_mask);
siginitset(&new_ka.sa.sa_mask, mask);
}
ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
if (!ret && oact) {
if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
__put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
__put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
return -EFAULT;
__put_user(old_ka.sa.sa_flags, &oact->sa_flags);
__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
}
return ret;
}
int sys_sigaltstack(const stack_t *uss, stack_t __user *uoss)
{
return do_sigaltstack(uss, uoss, rdusp());
}
/*
* Do a signal return; undo the signal stack.
*/
struct sigframe {
struct sigcontext sc;
unsigned long extramask[_NSIG_WORDS-1];
unsigned char retcode[8]; /* trampoline code */
};
struct rt_sigframe {
struct siginfo *pinfo;
void *puc;
struct siginfo info;
struct ucontext uc;
unsigned char retcode[8]; /* trampoline code */
};
static int
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
{
unsigned int err = 0;
unsigned long old_usp;
/* Always make any pending restarted system calls return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall;
/* restore the 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;
}
/* Define dummy arguments to be able to reach the regs argument. */
asmlinkage int sys_sigreturn(long r10, long r11, long r12, long r13, long mof,
long srp, struct pt_regs *regs)
{
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;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
if (restore_sigcontext(regs, &frame->sc))
goto badframe;
/* TODO: SIGTRAP when single-stepping as in arm ? */
return regs->r10;
badframe:
force_sig(SIGSEGV, current);
return 0;
}
/* Define dummy arguments to be able to reach the regs argument. */
asmlinkage int sys_rt_sigreturn(long r10, long r11, long r12, long r13,
long mof, long srp, struct pt_regs *regs)
{
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;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
goto badframe;
if (do_sigaltstack(&frame->uc.uc_stack, NULL, rdusp()) == -EFAULT)
goto badframe;
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 k_sigaction *ka, struct pt_regs *regs, size_t frame_size)
{
unsigned long sp = rdusp();
/* This is the X/Open sanctioned signal stack switching. */
if (ka->sa.sa_flags & SA_ONSTACK) {
if (! on_sig_stack(sp))
sp = current->sas_ss_sp + current->sas_ss_size;
}
/* make sure the frame is dword-aligned */
sp &= ~3;
return (void __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(int sig, struct k_sigaction *ka,
sigset_t *set, struct pt_regs *regs)
{
struct sigframe __user *frame;
unsigned long return_ip;
int err = 0;
frame = get_sigframe(ka, regs, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto give_sigsegv;
err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
if (err)
goto give_sigsegv;
if (_NSIG_WORDS > 1) {
err |= __copy_to_user(frame->extramask, &set->sig[1],
sizeof(frame->extramask));
}
if (err)
goto give_sigsegv;
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
if (ka->sa.sa_flags & SA_RESTORER) {
return_ip = (unsigned long)ka->sa.sa_restorer;
} else {
/* trampoline - the desired return ip is the retcode itself */
return_ip = (unsigned long)&frame->retcode;
/* This is movu.w __NR_sigreturn, 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)
goto give_sigsegv;
/* Set up registers for signal handler */
regs->irp = (unsigned long) ka->sa.sa_handler; /* what we enter NOW */
regs->srp = return_ip; /* what we enter LATER */
regs->r10 = sig; /* first argument is signo */
/* actually move the usp to reflect the stacked frame */
wrusp((unsigned long)frame);
return 0;
give_sigsegv:
force_sigsegv(sig, current);
return -EFAULT;
}
static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
sigset_t *set, struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
unsigned long return_ip;
int err = 0;
frame = get_sigframe(ka, regs, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto give_sigsegv;
err |= __put_user(&frame->info, &frame->pinfo);
err |= __put_user(&frame->uc, &frame->puc);
err |= copy_siginfo_to_user(&frame->info, info);
if (err)
goto give_sigsegv;
/* Clear all the bits of the ucontext we don't use. */
err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]);
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
if (err)
goto give_sigsegv;
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
if (ka->sa.sa_flags & SA_RESTORER) {
return_ip = (unsigned long)ka->sa.sa_restorer;
} else {
/* trampoline - the desired return ip is the retcode itself */
return_ip = (unsigned long)&frame->retcode;
/* This is movu.w __NR_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)
goto give_sigsegv;
/* TODO what is the current->exec_domain stuff and invmap ? */
/* Set up registers for signal handler */
/* What we enter NOW */
regs->irp = (unsigned long) ka->sa.sa_handler;
/* What we enter LATER */
regs->srp = return_ip;
/* First argument is signo */
regs->r10 = 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;
give_sigsegv:
force_sigsegv(sig, current);
return -EFAULT;
}
/*
* OK, we're invoking a handler
*/
static inline int handle_signal(int canrestart, unsigned long sig,
siginfo_t *info, struct k_sigaction *ka,
sigset_t *oldset, struct pt_regs *regs)
{
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 (!(ka->sa.sa_flags & SA_RESTART)) {
regs->r10 = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
/* ERESTARTNOINTR means that the syscall should
* be called again after the signal handler returns. */
RESTART_CRIS_SYS(regs);
}
}
/* Set up the stack frame */
if (ka->sa.sa_flags & SA_SIGINFO)
ret = setup_rt_frame(sig, ka, info, oldset, regs);
else
ret = setup_frame(sig, ka, oldset, regs);
if (ret == 0) {
spin_lock_irq(&current->sighand->siglock);
sigorsets(&current->blocked, &current->blocked,
&ka->sa.sa_mask);
if (!(ka->sa.sa_flags & SA_NODEFER))
sigaddset(&current->blocked, sig);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
}
return ret;
}
/*
* 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)
{
siginfo_t info;
int signr;
struct k_sigaction ka;
sigset_t *oldset;
/*
* 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 (test_thread_flag(TIF_RESTORE_SIGMASK))
oldset = &current->saved_sigmask;
else
oldset = &current->blocked;
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) {
/* Whee! Actually deliver the signal. */
if (handle_signal(canrestart, signr, &info, &ka,
oldset, regs)) {
/* a signal was successfully delivered; the saved
* sigmask will have been stored in the signal frame,
* and will be restored by sigreturn, so we can simply
* clear the TIF_RESTORE_SIGMASK flag */
if (test_thread_flag(TIF_RESTORE_SIGMASK))
clear_thread_flag(TIF_RESTORE_SIGMASK);
}
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 */
if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
clear_thread_flag(TIF_RESTORE_SIGMASK);
sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
}
}
+290
View File
@@ -0,0 +1,290 @@
/*
* 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 <arch/svinto.h>
#include <asm/types.h>
#include <asm/signal.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <asm/rtc.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
extern int set_rtc_mmss(unsigned long nowtime);
extern int have_rtc;
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;
}
unsigned long do_slow_gettimeoffset(void)
{
unsigned long 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 usec */
return (TIMER0_DIV - count) * ((NSEC_PER_SEC/1000)/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
#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
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) && !defined(CONFIG_SVINTO_SIM)
/* only keep watchdog happy as long as we have memory left! */
if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) {
/* reset the watchdog with the inverse of the old key */
watchdog_key ^= 0x7; /* invert key, which is 3 bits */
*R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, watchdog_key) |
IO_STATE(R_WATCHDOG, enable, start);
}
#endif
}
/* stop the watchdog - we still need the correct key */
void
stop_watchdog(void)
{
#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
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
}
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "xtime_update()" routine every clocktick
*/
//static unsigned short myjiff; /* used by our debug routine print_timestamp */
extern void cris_do_profile(struct pt_regs *regs);
static inline irqreturn_t
timer_interrupt(int irq, void *dev_id)
{
struct pt_regs *regs = 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
* it needs to be IRQF_DISABLED to make the jiffies update work properly
*/
static struct irqaction irq2 = {
.handler = timer_interrupt,
.flags = IRQF_SHARED | IRQF_DISABLED,
.name = "timer",
};
void __init
time_init(void)
{
/* probe for the RTC and read it if it exists
* Before the RTC can be probed the loops_per_usec variable needs
* to be initialized to make usleep work. A better value for
* loops_per_usec is calculated by the kernel later once the
* clock has started.
*/
loops_per_usec = 50;
if(RTC_INIT() < 0)
have_rtc = 0;
else
have_rtc = 1;
/* 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
*R_IRQ_MASK0_SET =
IO_STATE(R_IRQ_MASK0_SET, timer0, set); /* unmask the timer irq */
/* now actually register the timer irq handler that calls timer_interrupt() */
setup_irq(2, &irq2); /* irq 2 is the timer0 irq in etrax */
/* enable watchdog if we should use one */
#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
printk("Enabling watchdog...\n");
start_watchdog();
/* 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
}
+131
View File
@@ -0,0 +1,131 @@
/*
* Helper functions for trap handlers
*
* Copyright (C) 2000-2007, Axis Communications AB.
*
* Authors: Bjorn Wesen
* Hans-Peter Nilsson
*
*/
#include <linux/ptrace.h>
#include <asm/uaccess.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
+9
View File
@@ -0,0 +1,9 @@
#
# Makefile for Etrax-specific library files..
#
EXTRA_AFLAGS := -traditional
lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o
+118
View File
@@ -0,0 +1,118 @@
/*
* 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
@@ -0,0 +1,126 @@
/*
* 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
@@ -0,0 +1,64 @@
/*
* 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
+42
View File
@@ -0,0 +1,42 @@
/*
* memcpy for large blocks, using memory-memory DMA channels 6 and 7 in Etrax
*/
#include <asm/svinto.h>
#include <asm/io.h>
#define D(x)
void *dma_memcpy(void *pdst,
const void *psrc,
unsigned int pn)
{
static etrax_dma_descr indma, outdma;
D(printk(KERN_DEBUG "dma_memcpy %d bytes... ", pn));
#if 0
*R_GEN_CONFIG = genconfig_shadow =
(genconfig_shadow & ~0x3c0000) |
IO_STATE(R_GEN_CONFIG, dma6, intdma7) |
IO_STATE(R_GEN_CONFIG, dma7, intdma6);
#endif
indma.sw_len = outdma.sw_len = pn;
indma.ctrl = d_eol | d_eop;
outdma.ctrl = d_eol;
indma.buf = psrc;
outdma.buf = pdst;
*R_DMA_CH6_FIRST = &indma;
*R_DMA_CH7_FIRST = &outdma;
*R_DMA_CH6_CMD = IO_STATE(R_DMA_CH6_CMD, cmd, start);
*R_DMA_CH7_CMD = IO_STATE(R_DMA_CH7_CMD, cmd, start);
while (*R_DMA_CH7_CMD == 1)
/* wait for completion */;
D(printk(KERN_DEBUG "done\n"));
}
+150
View File
@@ -0,0 +1,150 @@
/*
* 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 decompresser to the kernel
*
* Copyright (C) 2000, 2001 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.
*/
;; 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.
#ifndef CONFIG_SVINTO_SIM
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
#endif
@@ -0,0 +1,60 @@
/*
* 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
+259
View File
@@ -0,0 +1,259 @@
/* 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;
}
@@ -0,0 +1,86 @@
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* IP/TCP/UDP checksumming routines
*
* Authors: Jorge Cwik, <jorge@laser.satlink.net>
* Arnt Gulbrandsen, <agulbra@nvg.unit.no>
* Tom May, <ftom@netcom.com>
* Lots of code moved from tcp.c and ip.c; see those files
* for more names.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <net/checksum.h>
#include <net/module.h>
#undef PROFILE_CHECKSUM
#ifdef PROFILE_CHECKSUM
/* these are just for profiling the checksum code with an oscillioscope.. uh */
#if 0
#define BITOFF *((unsigned char *)0xb0000030) = 0xff
#define BITON *((unsigned char *)0xb0000030) = 0x0
#endif
#include <asm/io.h>
#define CBITON LED_ACTIVE_SET(1)
#define CBITOFF LED_ACTIVE_SET(0)
#define BITOFF
#define BITON
#else
#define BITOFF
#define BITON
#define CBITOFF
#define CBITON
#endif
/*
* computes a partial checksum, e.g. for TCP/UDP fragments
*/
#include <asm/delay.h>
__wsum csum_partial(const void *p, int len, __wsum __sum)
{
u32 sum = (__force u32)__sum;
const u16 *buff = p;
/*
* Experiments with ethernet and slip connections show that buff
* is aligned on either a 2-byte or 4-byte boundary.
*/
const void *endMarker = p + len;
const void *marker = endMarker - (len % 16);
#if 0
if((int)buff & 0x3)
printk("unaligned buff %p\n", buff);
__delay(900); /* extra delay of 90 us to test performance hit */
#endif
BITON;
while (buff < marker) {
sum += *buff++;
sum += *buff++;
sum += *buff++;
sum += *buff++;
sum += *buff++;
sum += *buff++;
sum += *buff++;
sum += *buff++;
}
marker = endMarker - (len % 2);
while (buff < marker)
sum += *buff++;
if (endMarker > buff)
sum += *(const u8 *)buff; /* add extra byte separately */
BITOFF;
return (__force __wsum)sum;
}
EXPORT_SYMBOL(csum_partial);
+236
View File
@@ -0,0 +1,236 @@
/* 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;
}
+523
View File
@@ -0,0 +1,523 @@
/*
* User address space access functions.
* The non-inlined parts of asm-cris/uaccess.h are here.
*
* Copyright (C) 2000, Axis Communications AB.
*
* Written by Hans-Peter Nilsson.
* Pieces used from memcpy, originally by Kenny Ranerup long time ago.
*/
#include <asm/uaccess.h>
/* Asm:s have been tweaked (within the domain of correctness) to give
satisfactory results for "gcc version 2.96 20000427 (experimental)".
Check regularly...
Note that the PC saved at a bus-fault is the address *after* the
faulting instruction, which means the branch-target for instructions in
delay-slots for taken branches. Note also that the postincrement in
the instruction is performed regardless of bus-fault; the register is
seen updated in fault handlers.
Oh, and on the code formatting issue, to whomever feels like "fixing
it" to Conformity: I'm too "lazy", but why don't you go ahead and "fix"
string.c too. I just don't think too many people will hack this file
for the code format to be an issue. */
/* Copy to userspace. This is based on the memcpy used for
kernel-to-kernel copying; see "string.c". */
unsigned long
__copy_user (void __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;
}
/* Copy from user to kernel, zeroing the bytes that were inaccessible in
userland. The return-value is the number of bytes that were
inaccessible. */
unsigned long
__copy_user_zeroing(void *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 (((unsigned long) src & 2) && n >= 2)
{
__asm_copy_from_user_2 (dst, src, retn);
n -= 2;
}
/* We only need one check after the unalignment-adjustments, because
if both adjustments were done, either both or neither reference
had an exception. */
if (retn != 0)
goto copy_exception_bytes;
}
/* 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 copy_exception_bytes;
}
/* If we get here, there were no memory read faults. */
switch (n)
{
/* These copies are at least "naturally aligned" (so we don't have
to check each byte), due to the src alignment code before the
movem loop. The *_3 case *will* get the correct count for retn. */
case 0:
/* This case deliberately left in (if you have doubts check the
generated assembly code). */
break;
case 1:
__asm_copy_from_user_1 (dst, src, retn);
break;
case 2:
__asm_copy_from_user_2 (dst, src, retn);
break;
case 3:
__asm_copy_from_user_3 (dst, src, retn);
break;
}
/* If we get here, retn correctly reflects the number of failing
bytes. */
return retn;
copy_exception_bytes:
/* We already have "retn" bytes cleared, and need to clear the
remaining "n" bytes. A non-optimized simple byte-for-byte in-line
memset is preferred here, since this isn't speed-critical code and
we'd rather have this a leaf-function than calling memset. */
{
char *endp;
for (endp = dst + n; dst < endp; dst++)
*dst = 0;
}
return retn + n;
}
/* Zero userspace. */
unsigned long
__do_clear_user (void __user *pto, unsigned long pn)
{
/* We want the parameters put in special registers.
Make sure the compiler is able to make something useful of this.
As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop).
FIXME: Comment for old gcc version. Check.
If gcc was 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;
}
+6
View File
@@ -0,0 +1,6 @@
#
# Makefile for the linux cris-specific parts of the memory manager.
#
obj-y := fault.o init.o tlb.o
+95
View File
@@ -0,0 +1,95 @@
/*
* 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 <asm/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);
}
+263
View File
@@ -0,0 +1,263 @@
/*
* 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 */
#ifdef CONFIG_JULIETTE
IO_STATE(R_MMU_KSEG, seg_a, seg ) | /* ARTPEC etc. */
#else
IO_STATE(R_MMU_KSEG, seg_a, page ) |
#endif
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 ) |
#ifdef CONFIG_JULIETTE
IO_FIELD(R_MMU_KBASE_HI, base_a, 0xa ) |
#else
IO_FIELD(R_MMU_KBASE_HI, base_a, 0x0 ) |
#endif
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);
}
+176
View File
@@ -0,0 +1,176 @@
/*
* 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 <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);
}
}
+2
View File
@@ -0,0 +1,2 @@
/* At the time of this writing, there's no equivalent ld option. */
OUTPUT_ARCH (cris)