diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/cris/arch-v10 |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/cris/arch-v10')
57 files changed, 17255 insertions, 0 deletions
diff --git a/arch/cris/arch-v10/Kconfig b/arch/cris/arch-v10/Kconfig new file mode 100644 index 00000000000..2ca64cc40c6 --- /dev/null +++ b/arch/cris/arch-v10/Kconfig @@ -0,0 +1,422 @@ +# 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:include/asm-cris/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:include/asm-cris/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:include/asm-cris/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 "Eigth 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 debug-port" + depends on ETRAX_ARCH_V10 + default ETRAX_DEBUG_PORT0 + +config ETRAX_DEBUG_PORT0 + bool "Serial-0" + help + Choose a serial port for the ETRAX debug console. Default to + port 0. + +config ETRAX_DEBUG_PORT1 + bool "Serial-1" + help + Use serial port 1 for the console. + +config ETRAX_DEBUG_PORT2 + bool "Serial-2" + help + Use serial port 2 for the console. + +config ETRAX_DEBUG_PORT3 + bool "Serial-3" + help + Use serial port 3 for the console. + +config ETRAX_DEBUG_PORT_NULL + bool "disabled" + help + Disable serial-port debugging. + +endchoice + +choice + prompt "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 peripherials (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. diff --git a/arch/cris/arch-v10/README.mm b/arch/cris/arch-v10/README.mm new file mode 100644 index 00000000000..6f08903f313 --- /dev/null +++ b/arch/cris/arch-v10/README.mm @@ -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 comparision, 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: + +extern 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: + +extern 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: + +extern 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. + + + diff --git a/arch/cris/arch-v10/boot/Makefile b/arch/cris/arch-v10/boot/Makefile new file mode 100644 index 00000000000..fe6650368e6 --- /dev/null +++ b/arch/cris/arch-v10/boot/Makefile @@ -0,0 +1,12 @@ +# +# arch/cris/boot/Makefile +# + +zImage: compressed/vmlinuz + +compressed/vmlinuz: $(TOPDIR)/vmlinux + @$(MAKE) -C compressed vmlinuz + +clean: + rm -f zImage tools/build compressed/vmlinux.out + @$(MAKE) -C compressed clean diff --git a/arch/cris/arch-v10/boot/compressed/Makefile b/arch/cris/arch-v10/boot/compressed/Makefile new file mode 100644 index 00000000000..5f71c2c819e --- /dev/null +++ b/arch/cris/arch-v10/boot/compressed/Makefile @@ -0,0 +1,40 @@ +# +# linux/arch/etrax100/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux files and romfs +# + +CC = gcc-cris -melf -I $(TOPDIR)/include +CFLAGS = -O2 +LD = ld-cris +OBJCOPY = objcopy-cris +OBJCOPYFLAGS = -O binary --remove-section=.bss +OBJECTS = head.o misc.o + +# files to compress +SYSTEM = $(TOPDIR)/vmlinux.bin + +all: vmlinuz + +decompress.bin: $(OBJECTS) + $(LD) -T decompress.ld -o decompress.o $(OBJECTS) + $(OBJCOPY) $(OBJCOPYFLAGS) decompress.o decompress.bin +# save it for mkprod in the topdir. + cp decompress.bin $(TOPDIR) + + +vmlinuz: piggy.img decompress.bin + cat decompress.bin piggy.img > vmlinuz + rm -f piggy.img + +head.o: head.S + $(CC) -D__ASSEMBLY__ -traditional -c head.S -o head.o + +# gzip the kernel image + +piggy.img: $(SYSTEM) + cat $(SYSTEM) | gzip -f -9 > piggy.img + +clean: + rm -f piggy.img vmlinuz vmlinuz.o + diff --git a/arch/cris/arch-v10/boot/compressed/README b/arch/cris/arch-v10/boot/compressed/README new file mode 100644 index 00000000000..48b3db9924b --- /dev/null +++ b/arch/cris/arch-v10/boot/compressed/README @@ -0,0 +1,25 @@ +Creation of the self-extracting compressed kernel image (vmlinuz) +----------------------------------------------------------------- +$Id: README,v 1.1 2001/12/17 13:59:27 bjornw Exp $ + +This can be slightly confusing because it's a process with many steps. + +The kernel object built by the arch/etrax100/Makefile, vmlinux, is split +by that makefile into text and data binary files, vmlinux.text and +vmlinux.data. + +Those files together with a ROM filesystem can be catted together and +burned into a flash or executed directly at the DRAM origin. + +They can also be catted together and compressed with gzip, which is what +happens in this makefile. Together they make up piggy.img. + +The decompressor is built into the file decompress.o. It is turned into +the binary file decompress.bin, which is catted together with piggy.img +into the file vmlinuz. It can be executed in an arbitrary place in flash. + +Be careful - it assumes some things about free locations in DRAM. It +assumes the DRAM starts at 0x40000000 and that it is at least 8 MB, +so it puts its code at 0x40700000, and initial stack at 0x40800000. + +-Bjorn diff --git a/arch/cris/arch-v10/boot/compressed/decompress.ld b/arch/cris/arch-v10/boot/compressed/decompress.ld new file mode 100644 index 00000000000..0b0a14fe617 --- /dev/null +++ b/arch/cris/arch-v10/boot/compressed/decompress.ld @@ -0,0 +1,29 @@ +OUTPUT_FORMAT(elf32-us-cris) + +MEMORY + { + dram : ORIGIN = 0x40700000, + LENGTH = 0x00100000 + } + +SECTIONS +{ + .text : + { + _stext = . ; + *(.text) + *(.rodata) + *(.rodata.*) + _etext = . ; + } > dram + .data : + { + *(.data) + _edata = . ; + } > dram + .bss : + { + *(.bss) + _end = ALIGN( 0x10 ) ; + } > dram +} diff --git a/arch/cris/arch-v10/boot/compressed/head.S b/arch/cris/arch-v10/boot/compressed/head.S new file mode 100644 index 00000000000..4cbdd4b1d9d --- /dev/null +++ b/arch/cris/arch-v10/boot/compressed/head.S @@ -0,0 +1,111 @@ +/* + * arch/cris/boot/compressed/head.S + * + * Copyright (C) 1999, 2001 Axis Communications AB + * + * Code that sets up the DRAM registers, calls the + * decompressor to unpack the piggybacked kernel, and jumps. + * + */ + +#include <linux/config.h> +#define ASSEMBLER_MACROS_ONLY +#include <asm/arch/sv_addr_ag.h> + +#define RAM_INIT_MAGIC 0x56902387 + + ;; Exported symbols + + .globl _input_data + + + .text + + nop + di + +;; 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: + + ;; Initiate the PA and PB ports + + move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, r0 + move.b r0, [R_PORT_PA_DATA] + + move.b CONFIG_ETRAX_DEF_R_PORT_PA_DIR, r0 + move.b r0, [R_PORT_PA_DIR] + + move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, r0 + move.b r0, [R_PORT_PB_DATA] + + move.b CONFIG_ETRAX_DEF_R_PORT_PB_DIR, r0 + move.b r0, [R_PORT_PB_DIR] + + ;; Setup the stack to a suitably high address. + ;; We assume 8 MB is the minimum DRAM in an eLinux + ;; product and put the sp at the top for now. + + move.d 0x40800000, sp + + ;; Figure out where the compressed piggyback image is + ;; in the flash (since we wont try to copy it to DRAM + ;; before unpacking). It is at _edata, but in flash. + ;; Use (_edata - basse) as offset to the current PC. + +basse: move.d pc, r5 + and.d 0x7fffffff, r5 ; strip any non-cache bit + subq 2, r5 ; compensate for the move.d pc instr + move.d r5, r0 ; save for later - flash address of 'basse' + add.d _edata, r5 + sub.d basse, r5 ; r5 = flash address of '_edata' + + ;; Copy text+data to DRAM + + move.d basse, r1 ; destination + move.d _edata, r2 ; end destination +1: move.w [r0+], r3 + move.w r3, [r1+] + cmp.d r2, r1 + bcs 1b + nop + + move.d r5, [_input_data] ; for the decompressor + + + ;; Clear the decompressors BSS (between _edata and _end) + + moveq 0, r0 + move.d _edata, r1 + move.d _end, r2 +1: move.w r0, [r1+] + cmp.d r2, r1 + bcs 1b + nop + + ;; Do the decompression and save compressed size in _inptr + + jsr _decompress_kernel + + ;; Put start address of root partition in r9 so the kernel can use it + ;; when mounting from flash + + move.d [_input_data], r9 ; flash address of compressed kernel + add.d [_inptr], r9 ; size of compressed kernel + + ;; Enter the decompressed kernel + move.d RAM_INIT_MAGIC, r8 ; Tell kernel that DRAM is initialized + jump 0x40004000 ; kernel is linked to this address + + .data + +_input_data: + .dword 0 ; used by the decompressor + +#include "../../lib/hw_settings.S" diff --git a/arch/cris/arch-v10/boot/compressed/misc.c b/arch/cris/arch-v10/boot/compressed/misc.c new file mode 100644 index 00000000000..1b5e83f1f84 --- /dev/null +++ b/arch/cris/arch-v10/boot/compressed/misc.c @@ -0,0 +1,273 @@ +/* + * misc.c + * + * $Id: misc.c,v 1.6 2003/10/27 08:04:31 starvik Exp $ + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * puts by Nick Holloway 1993, better puts by Martin Mares 1995 + * adoptation for Linux/CRIS Axis Communications AB, 1999 + * + */ + +/* where the piggybacked kernel image expects itself to live. + * it is the same address we use when we network load an uncompressed + * image into DRAM, and it is the address the kernel is linked to live + * at by vmlinux.lds.S + */ + +#define KERNEL_LOAD_ADR 0x40004000 + +#include <linux/config.h> + +#include <linux/types.h> +#include <asm/arch/svinto.h> + +/* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +void* memset(void* s, int c, size_t n); +void* memcpy(void* __dest, __const void* __src, + size_t __n); + +#define memzero(s, n) memset ((s), 0, (n)) + + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, */ + /* and a power of two */ + +static uch *inbuf; /* input buffer */ +static uch window[WSIZE]; /* Sliding window buffer */ + +unsigned inptr = 0; /* index of next byte to be processed in inbuf + * After decompression it will contain the + * compressed size, and head.S will read it. + */ + +static unsigned outcnt = 0; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +#define get_byte() inbuf[inptr++] + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +extern char *input_data; /* lives in head.S */ + +static long bytes_out = 0; +static uch *output_data; +static unsigned long output_ptr = 0; + +static void *malloc(int size); +static void free(void *where); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +static void puts(const char *); + +/* the "heap" is put directly after the BSS ends, at end */ + +extern int end; +static long free_mem_ptr = (long)&end; + +#include "../../../../../lib/inflate.c" + +static void *malloc(int size) +{ + void *p; + + if (size <0) error("Malloc error"); + + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *)free_mem_ptr; + free_mem_ptr += size; + + return p; +} + +static void free(void *where) +{ /* Don't care */ +} + +static void gzip_mark(void **ptr) +{ + *ptr = (void *) free_mem_ptr; +} + +static void gzip_release(void **ptr) +{ + free_mem_ptr = (long) *ptr; +} + +/* decompressor info and error messages to serial console */ + +static void +puts(const char *s) +{ +#ifndef CONFIG_ETRAX_DEBUG_PORT_NULL + while(*s) { +#ifdef CONFIG_ETRAX_DEBUG_PORT0 + while(!(*R_SERIAL0_STATUS & (1 << 5))) ; + *R_SERIAL0_TR_DATA = *s++; +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT1 + while(!(*R_SERIAL1_STATUS & (1 << 5))) ; + *R_SERIAL1_TR_DATA = *s++; +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT2 + while(!(*R_SERIAL2_STATUS & (1 << 5))) ; + *R_SERIAL2_TR_DATA = *s++; +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT3 + while(!(*R_SERIAL3_STATUS & (1 << 5))) ; + *R_SERIAL3_TR_DATA = *s++; +#endif + } +#endif +} + +void* +memset(void* s, int c, size_t n) +{ + int i; + char *ss = (char*)s; + + for (i=0;i<n;i++) ss[i] = c; +} + +void* +memcpy(void* __dest, __const void* __src, + size_t __n) +{ + int i; + char *d = (char *)__dest, *s = (char *)__src; + + for (i=0;i<__n;i++) d[i] = s[i]; +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ + +static void +flush_window() +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, *out, ch; + + in = window; + out = &output_data[output_ptr]; + for (n = 0; n < outcnt; n++) { + ch = *out++ = *in++; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +} + +static void +error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted\n"); + + while(1); /* Halt */ +} + +void +setup_normal_output_buffer() +{ + output_data = (char *)KERNEL_LOAD_ADR; +} + +void +decompress_kernel() +{ + char revision; + + /* input_data is set in head.S */ + inbuf = input_data; + +#ifdef CONFIG_ETRAX_DEBUG_PORT0 + *R_SERIAL0_XOFF = 0; + *R_SERIAL0_BAUD = 0x99; + *R_SERIAL0_TR_CTRL = 0x40; +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT1 + *R_SERIAL1_XOFF = 0; + *R_SERIAL1_BAUD = 0x99; + *R_SERIAL1_TR_CTRL = 0x40; +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT2 + *R_GEN_CONFIG = 0x08; + *R_SERIAL2_XOFF = 0; + *R_SERIAL2_BAUD = 0x99; + *R_SERIAL2_TR_CTRL = 0x40; +#endif +#ifdef CONFIG_ETRAX_DEBUG_PORT3 + *R_GEN_CONFIG = 0x100; + *R_SERIAL3_XOFF = 0; + *R_SERIAL3_BAUD = 0x99; + *R_SERIAL3_TR_CTRL = 0x40; +#endif + + setup_normal_output_buffer(); + + makecrc(); + + __asm__ volatile ("move vr,%0" : "=rm" (revision)); + if (revision < 10) + { + puts("You need an ETRAX 100LX to run linux 2.6\n"); + while(1); + } + + puts("Uncompressing Linux...\n"); + gunzip(); + puts("Done. Now booting the kernel.\n"); +} diff --git a/arch/cris/arch-v10/boot/rescue/Makefile b/arch/cris/arch-v10/boot/rescue/Makefile new file mode 100644 index 00000000000..e9f2ba2ad02 --- /dev/null +++ b/arch/cris/arch-v10/boot/rescue/Makefile @@ -0,0 +1,55 @@ +# +# Makefile for rescue code +# +ifndef TOPDIR +TOPDIR = ../../../.. +endif +CC = gcc-cris -mlinux -I $(TOPDIR)/include +CFLAGS = -O2 +LD = gcc-cris -mlinux -nostdlib +OBJCOPY = objcopy-cris +OBJCOPYFLAGS = -O binary --remove-section=.bss + +all: rescue.bin testrescue.bin kimagerescue.bin + +rescue: rescue.bin + # do nothing + +rescue.bin: head.o + $(LD) -T rescue.ld -o rescue.o head.o + $(OBJCOPY) $(OBJCOPYFLAGS) rescue.o rescue.bin + cp rescue.bin $(TOPDIR) + +testrescue.bin: testrescue.o + $(OBJCOPY) $(OBJCOPYFLAGS) testrescue.o tr.bin +# Pad it to 784 bytes + dd if=/dev/zero of=tmp2423 bs=1 count=784 + cat tr.bin tmp2423 >testrescue_tmp.bin + dd if=testrescue_tmp.bin of=testrescue.bin bs=1 count=784 + rm tr.bin tmp2423 testrescue_tmp.bin + +kimagerescue.bin: kimagerescue.o + $(OBJCOPY) $(OBJCOPYFLAGS) kimagerescue.o ktr.bin +# Pad it to 784 bytes, that's what the rescue loader expects + dd if=/dev/zero of=tmp2423 bs=1 count=784 + cat ktr.bin tmp2423 >kimagerescue_tmp.bin + dd if=kimagerescue_tmp.bin of=kimagerescue.bin bs=1 count=784 + rm ktr.bin tmp2423 kimagerescue_tmp.bin + +head.o: head.S + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o + +testrescue.o: testrescue.S + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o + +kimagerescue.o: kimagerescue.S + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o + +clean: + rm -f *.o *.bin + +fastdep: + +modules: + +modules-install: diff --git a/arch/cris/arch-v10/boot/rescue/head.S b/arch/cris/arch-v10/boot/rescue/head.S new file mode 100644 index 00000000000..8689ea972c4 --- /dev/null +++ b/arch/cris/arch-v10/boot/rescue/head.S @@ -0,0 +1,333 @@ +/* $Id: head.S,v 1.6 2003/04/09 08:12:43 pkj Exp $ + * + * Rescue code, made to reside at the beginning of the + * flash-memory. when it starts, it checks a partition + * table at the first sector after the rescue sector. + * the partition table was generated by the product builder + * script and contains offsets, lengths, types and checksums + * for each partition that this code should check. + * + * If any of the checksums fail, we assume the flash is so + * corrupt that we cant use it to boot into the ftp flash + * loader, and instead we initialize the serial port to + * receive a flash-loader and new flash image. we dont include + * any flash code here, but just accept a certain amount of + * bytes from the serial port and jump into it. the downloaded + * code is put in the cache. + * + * The partitiontable is designed so that it is transparent to + * code execution - it has a relative branch opcode in the + * beginning that jumps over it. each entry contains extra + * data so we can add stuff later. + * + * Partition table format: + * + * Code transparency: + * + * 2 bytes [opcode 'nop'] + * 2 bytes [opcode 'di'] + * 4 bytes [opcode 'ba <offset>', 8-bit or 16-bit version] + * 2 bytes [opcode 'nop', delay slot] + * + * Table validation (at +10): + * + * 2 bytes [magic/version word for partitiontable - 0xef, 0xbe] + * 2 bytes [length of all entries plus the end marker] + * 4 bytes [checksum for the partitiontable itself] + * + * Entries, each with the following format, last has offset -1: + * + * 4 bytes [offset in bytes, from start of flash] + * 4 bytes [length in bytes of partition] + * 4 bytes [checksum, simple longword sum] + * 2 bytes [partition type] + * 2 bytes [flags, only bit 0 used, ro/rw = 1/0] + * 16 bytes [reserved for future use] + * + * End marker + * + * 4 bytes [-1] + * + * 10 bytes [0, padding] + * + * Bit 0 in flags signifies RW or RO. The rescue code only bothers + * to check the checksum for RO partitions, since the others will + * change their data without updating the checksums. A 1 in bit 0 + * means RO, 0 means RW. That way, it is possible to set a partition + * in RO mode initially, and later mark it as RW, since you can always + * write 0's to the flash. + * + * During the wait for serial input, the status LED will flash so the + * user knows something went wrong. + * + * Copyright (C) 1999, 2000, 2001, 2002, 2003 Axis Communications AB + */ + +#include <linux/config.h> +#define ASSEMBLER_MACROS_ONLY +#include <asm/arch/sv_addr_ag.h> + + ;; The partitiontable is looked for at the first sector after the boot + ;; sector. Sector size is 65536 bytes in all flashes we use. + +#define PTABLE_START CONFIG_ETRAX_PTABLE_SECTOR +#define PTABLE_MAGIC 0xbeef + + ;; The normal Etrax100 on-chip boot ROM does serial boot at 0x380000f0. + ;; That is not where we put our downloaded serial boot-code. The length is + ;; enough for downloading code that loads the rest of itself (after + ;; having setup the DRAM etc). It is the same length as the on-chip + ;; ROM loads, so the same host loader can be used to load a rescued + ;; product as well as one booted through the Etrax serial boot code. + +#define CODE_START 0x40000000 +#define CODE_LENGTH 784 + +#ifdef CONFIG_ETRAX_RESCUE_SER0 +#define SERXOFF R_SERIAL0_XOFF +#define SERBAUD R_SERIAL0_BAUD +#define SERRECC R_SERIAL0_REC_CTRL +#define SERRDAT R_SERIAL0_REC_DATA +#define SERSTAT R_SERIAL0_STATUS +#endif +#ifdef CONFIG_ETRAX_RESCUE_SER1 +#define SERXOFF R_SERIAL1_XOFF +#define SERBAUD R_SERIAL1_BAUD +#define SERRECC R_SERIAL1_REC_CTRL +#define SERRDAT R_SERIAL1_REC_DATA +#define SERSTAT R_SERIAL1_STATUS +#endif +#ifdef CONFIG_ETRAX_RESCUE_SER2 +#define SERXOFF R_SERIAL2_XOFF +#define SERBAUD R_SERIAL2_BAUD +#define SERRECC R_SERIAL2_REC_CTRL +#define SERRDAT R_SERIAL2_REC_DATA +#define SERSTAT R_SERIAL2_STATUS +#endif +#ifdef CONFIG_ETRAX_RESCUE_SER3 +#define SERXOFF R_SERIAL3_XOFF +#define SERBAUD R_SERIAL3_BAUD +#define SERRECC R_SERIAL3_REC_CTRL +#define SERRDAT R_SERIAL3_REC_DATA +#define SERSTAT R_SERIAL3_STATUS +#endif + +#define NOP_DI 0xf025050f +#define RAM_INIT_MAGIC 0x56902387 + + .text + + ;; This is the entry point of the rescue code + ;; 0x80000000 if loaded in flash (as it should be) + ;; since etrax actually starts at address 2 when booting from flash, we + ;; put a nop (2 bytes) here first so we dont accidentally skip the di + + nop + di + + jump in_cache ; enter cached area instead +in_cache: + + ;; first put a jump test to give a possibility of upgrading the rescue code + ;; without erasing/reflashing the sector. we put a longword of -1 here and if + ;; it is not -1, we jump using the value as jump target. since we can always + ;; change 1's to 0's without erasing the sector, it is possible to add new + ;; code after this and altering the jumptarget in an upgrade. + +jtcd: move.d [jumptarget], $r0 + cmp.d 0xffffffff, $r0 + beq no_newjump + nop + + jump [$r0] + +jumptarget: + .dword 0xffffffff ; can be overwritten later to insert new code + +no_newjump: +#ifdef CONFIG_ETRAX_ETHERNET + ;; Start MII clock to make sure it is running when tranceiver is reset + move.d 0x3, $r0 ; enable = on, phy = mii_clk + move.d $r0, [R_NETWORK_GEN_CONFIG] +#endif + + ;; We need to setup the bus registers before we start using the DRAM +#include "../../lib/dram_init.S" + + ;; we now should go through the checksum-table and check the listed + ;; partitions for errors. + + move.d PTABLE_START, $r3 + move.d [$r3], $r0 + cmp.d NOP_DI, $r0 ; make sure the nop/di is there... + bne do_rescue + nop + + ;; skip the code transparency block (10 bytes). + + addq 10, $r3 + + ;; check for correct magic + + move.w [$r3+], $r0 + cmp.w PTABLE_MAGIC, $r0 + bne do_rescue ; didn't recognize - trig rescue + nop + + ;; check for correct ptable checksum + + movu.w [$r3+], $r2 ; ptable length + move.d $r2, $r8 ; save for later, length of total ptable + addq 28, $r8 ; account for the rest + move.d [$r3+], $r4 ; ptable checksum + move.d $r3, $r1 + jsr checksum ; r1 source, r2 length, returns in r0 + + cmp.d $r0, $r4 + bne do_rescue ; didn't match - trig rescue + nop + + ;; ptable is ok. validate each entry. + + moveq -1, $r7 + +ploop: move.d [$r3+], $r1 ; partition offset (from ptable start) + bne notfirst ; check if it is the partition containing ptable + nop ; yes.. + move.d $r8, $r1 ; for its checksum check, skip the ptable + move.d [$r3+], $r2 ; partition length + sub.d $r8, $r2 ; minus the ptable length + ba bosse + nop +notfirst: + cmp.d -1, $r1 ; the end of the ptable ? + beq flash_ok ; if so, the flash is validated + move.d [$r3+], $r2 ; partition length +bosse: move.d [$r3+], $r5 ; checksum + move.d [$r3+], $r4 ; type and flags + addq 16, $r3 ; skip the reserved bytes + btstq 16, $r4 ; check ro flag + bpl ploop ; rw partition, skip validation + nop + btstq 17, $r4 ; check bootable flag + bpl 1f + nop + move.d $r1, $r7 ; remember boot partition offset +1: + + add.d PTABLE_START, $r1 + + jsr checksum ; checksum the partition + + cmp.d $r0, $r5 + beq ploop ; checksums matched, go to next entry + nop + + ;; otherwise fall through to the rescue code. + +do_rescue: + ;; setup port PA and PB default initial directions and data + ;; (so we can flash LEDs, and so that DTR and others are set) + + move.b CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0 + move.b $r0, [R_PORT_PA_DIR] + move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0 + move.b $r0, [R_PORT_PA_DATA] + + move.b CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0 + move.b $r0, [R_PORT_PB_DIR] + move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0 + move.b $r0, [R_PORT_PB_DATA] + + ;; setup the serial port at 115200 baud + + moveq 0, $r0 + move.d $r0, [SERXOFF] + + move.b 0x99, $r0 + move.b $r0, [SERBAUD] ; 115.2kbaud for both transmit and receive + + move.b 0x40, $r0 ; rec enable + move.b $r0, [SERRECC] + + moveq 0, $r1 ; "timer" to clock out a LED red flash + move.d CODE_START, $r3 ; destination counter + movu.w CODE_LENGTH, $r4; length + +wait_ser: + addq 1, $r1 +#ifndef CONFIG_ETRAX_NO_LEDS +#ifdef CONFIG_ETRAX_PA_LEDS + move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r2 +#endif +#ifdef CONFIG_ETRAX_PB_LEDS + move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r2 +#endif + move.d (1 << CONFIG_ETRAX_LED1R) | (1 << CONFIG_ETRAX_LED2R), $r0 + btstq 16, $r1 + bpl 1f + nop + or.d $r0, $r2 ; set bit + ba 2f + nop +1: not $r0 ; clear bit + and.d $r0, $r2 +2: +#ifdef CONFIG_ETRAX_PA_LEDS + move.b $r2, [R_PORT_PA_DATA] +#endif +#ifdef CONFIG_ETRAX_PB_LEDS + move.b $r2, [R_PORT_PB_DATA] +#endif +#ifdef CONFIG_ETRAX_90000000_LEDS + move.b $r2, [0x90000000] +#endif +#endif + + ;; check if we got something on the serial port + + move.b [SERSTAT], $r0 + btstq 0, $r0 ; data_avail + bpl wait_ser + nop + + ;; got something - copy the byte and loop + + move.b [SERRDAT], $r0 + move.b $r0, [$r3+] + + subq 1, $r4 ; decrease length + bne wait_ser + nop + + ;; jump into downloaded code + + move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is initialized + jump CODE_START + +flash_ok: + ;; check r7, which contains either -1 or the partition to boot from + + cmp.d -1, $r7 + bne 1f + nop + move.d PTABLE_START, $r7; otherwise use the ptable start +1: + move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is initialized + jump $r7 ; boot! + + + ;; Helper subroutines + + ;; Will checksum by simple addition + ;; r1 - source + ;; r2 - length in bytes + ;; result will be in r0 +checksum: + moveq 0, $r0 +1: addu.b [$r1+], $r0 + subq 1, $r2 + bne 1b + nop + ret + nop diff --git a/arch/cris/arch-v10/boot/rescue/kimagerescue.S b/arch/cris/arch-v10/boot/rescue/kimagerescue.S new file mode 100644 index 00000000000..264bf7afc9a --- /dev/null +++ b/arch/cris/arch-v10/boot/rescue/kimagerescue.S @@ -0,0 +1,144 @@ +/* $Id: kimagerescue.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ + * + * Rescue code to be prepended on a kimage and copied to the + * rescue serial port. + * This is called from the rescue code, it will copy received data to + * 4004000 and after a timeout jump to it. + */ + +#include <linux/config.h> +#define ASSEMBLER_MACROS_ONLY +#include <asm/sv_addr_ag.h> + +#define CODE_START 0x40004000 +#define CODE_LENGTH 784 +#define TIMEOUT_VALUE 1000 + + +#ifdef CONFIG_ETRAX_RESCUE_SER0 +#define SERXOFF R_SERIAL0_XOFF +#define SERBAUD R_SERIAL0_BAUD +#define SERRECC R_SERIAL0_REC_CTRL +#define SERRDAT R_SERIAL0_REC_DATA +#define SERSTAT R_SERIAL0_STATUS +#endif +#ifdef CONFIG_ETRAX_RESCUE_SER1 +#define SERXOFF R_SERIAL1_XOFF +#define SERBAUD R_SERIAL1_BAUD +#define SERRECC R_SERIAL1_REC_CTRL +#define SERRDAT R_SERIAL1_REC_DATA +#define SERSTAT R_SERIAL1_STATUS +#endif +#ifdef CONFIG_ETRAX_RESCUE_SER2 +#define SERXOFF R_SERIAL2_XOFF +#define SERBAUD R_SERIAL2_BAUD +#define SERRECC R_SERIAL2_REC_CTRL +#define SERRDAT R_SERIAL2_REC_DATA +#define SERSTAT R_SERIAL2_STATUS +#endif +#ifdef CONFIG_ETRAX_RESCUE_SER3 +#define SERXOFF R_SERIAL3_XOFF +#define SERBAUD R_SERIAL3_BAUD +#define SERRECC R_SERIAL3_REC_CTRL +#define SERRDAT R_SERIAL3_REC_DATA +#define SERSTAT R_SERIAL3_STATUS +#endif + + .text + ;; This is the entry point of the rescue code + ;; 0x80000000 if loaded in flash (as it should be) + ;; since etrax actually starts at address 2 when booting from flash, we + ;; put a nop (2 bytes) here first so we dont accidentally skip the di + + nop + di +#ifndef CONFIG_SVINTO_SIM + ;; setup port PA and PB default initial directions and data + ;; (so we can flash LEDs, and so that DTR and others are set) + + move.b CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0 + move.b $r0, [R_PORT_PA_DIR] + move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0 + move.b $r0, [R_PORT_PA_DATA] + + move.b CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0 + move.b $r0, [R_PORT_PB_DIR] + move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0 + move.b $r0, [R_PORT_PB_DATA] + + ;; We need to setup the bus registers before we start using the DRAM +#include "../../lib/dram_init.S" + +#endif + ;; Setup the stack to a suitably high address. + ;; We assume 8 MB is the minimum DRAM in an eLinux + ;; product and put the sp at the top for now. + + move.d 0x40800000, $sp + + ;; setup the serial port at 115200 baud + + moveq 0, $r0 + move.d $r0, [SERXOFF] + + move.b 0x99, $r0 + move.b $r0, [SERBAUD] ; 115.2kbaud for both transmit and receive + + move.b 0x40, $r0 ; rec enable + move.b $r0, [SERRECC] + + + moveq 0, $r1 ; "timer" to clock out a LED red flash + move.d CODE_START, $r3 ; destination counter + move.d CODE_LENGTH, $r4 ; length + move.d TIMEOUT_VALUE, $r5 ; "timeout" until jump + +wait_ser: + addq 1, $r1 + subq 1, $r5 ; decrease timeout + beq jump_start ; timed out + nop +#ifndef CONFIG_ETRAX_NO_LEDS +#ifdef CONFIG_ETRAX_PA_LEDS + move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r2 +#endif +#ifdef CONFIG_ETRAX_PB_LEDS + move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r2 +#endif + move.d (1 << CONFIG_ETRAX_LED1R) | (1 << CONFIG_ETRAX_LED2R), $r0 + btstq 16, $r1 + bpl 1f + nop + or.d $r0, $r2 ; set bit + ba 2f + nop +1: not $r0 ; clear bit + and.d $r0, $r2 +2: +#ifdef CONFIG_ETRAX_PA_LEDS + move.b $r2, [R_PORT_PA_DATA] +#endif +#ifdef CONFIG_ETRAX_PB_LEDS + move.b $r2, [R_PORT_PB_DATA] +#endif +#endif + + ;; check if we got something on the serial port + + move.b [SERSTAT], $r0 + btstq 0, $r0 ; data_avail + bpl wait_ser + nop + + ;; got something - copy the byte and loop + + move.b [SERRDAT], $r0 + move.b $r0, [$r3+] + move.d TIMEOUT_VALUE, $r5 ; reset "timeout" + subq 1, $r4 ; decrease length + bne wait_ser + nop +jump_start: + ;; jump into downloaded code + + jump CODE_START diff --git a/arch/cris/arch-v10/boot/rescue/rescue.ld b/arch/cris/arch-v10/boot/rescue/rescue.ld new file mode 100644 index 00000000000..0b52a9490db --- /dev/null +++ b/arch/cris/arch-v10/boot/rescue/rescue.ld @@ -0,0 +1,20 @@ +MEMORY + { + flash : ORIGIN = 0x00000000, + LENGTH = 0x00100000 + } + +SECTIONS +{ + .text : + { + stext = . ; + *(.text) + etext = . ; + } > flash + .data : + { + *(.data) + edata = . ; + } > flash +} diff --git a/arch/cris/arch-v10/boot/rescue/testrescue.S b/arch/cris/arch-v10/boot/rescue/testrescue.S new file mode 100644 index 00000000000..566a9f34125 --- /dev/null +++ b/arch/cris/arch-v10/boot/rescue/testrescue.S @@ -0,0 +1,26 @@ +/* $Id: testrescue.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ + * + * Simple testcode to download by the rescue block. + * Just lits some LEDs to show it was downloaded correctly. + * + * Copyright (C) 1999 Axis Communications AB + */ + +#define ASSEMBLER_MACROS_ONLY +#include <asm/sv_addr_ag.h> + + .text + + nop + nop + moveq -1, $r2 + move.b $r2, [R_PORT_PA_DIR] + moveq 0, $r2 + move.b $r2, [R_PORT_PA_DATA] + +endless: + nop + ba endless + nop + + diff --git a/arch/cris/arch-v10/boot/tools/build.c b/arch/cris/arch-v10/boot/tools/build.c new file mode 100644 index 00000000000..2f9bbb26d60 --- /dev/null +++ b/arch/cris/arch-v10/boot/tools/build.c @@ -0,0 +1,288 @@ +/* + * linux/tools/build.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * This file builds a disk-image from three different files: + * + * - bootsect: exactly 512 bytes of 8086 machine code, loads the rest + * - setup: 8086 machine code, sets up system parm + * - system: 80386 code for actual system + * + * It does some checking that all files are of the correct type, and + * just writes the result to stdout, removing headers and padding to + * the right amount. It also writes some system data to stderr. + */ + +/* + * Changes by tytso to allow root device specification + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + * Cross compiling fixes by Gertjan van Wingerde, July 1996 + */ + +#include <stdio.h> /* fprintf */ +#include <string.h> +#include <stdlib.h> /* contains exit */ +#include <sys/types.h> /* unistd.h needs this */ +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <unistd.h> /* contains read/write */ +#include <fcntl.h> +#include <linux/a.out.h> +#include <errno.h> + +#define MINIX_HEADER 32 + +#define N_MAGIC_OFFSET 1024 +#ifndef __BFD__ +static int GCC_HEADER = sizeof(struct exec); +#endif + +#ifdef __BIG_KERNEL__ +#define SYS_SIZE 0xffff +#else +#define SYS_SIZE DEF_SYSSIZE +#endif + +#define DEFAULT_MAJOR_ROOT 0 +#define DEFAULT_MINOR_ROOT 0 + +/* max nr of sectors of setup: don't change unless you also change + * bootsect etc */ +#define SETUP_SECTS 4 + +#define STRINGIFY(x) #x + +typedef union { + int i; + long l; + short s[2]; + char b[4]; +} conv; + +long intel_long(long l) +{ + conv t; + + t.b[0] = l & 0xff; l >>= 8; + t.b[1] = l & 0xff; l >>= 8; + t.b[2] = l & 0xff; l >>= 8; + t.b[3] = l & 0xff; l >>= 8; + return t.l; +} + +int intel_int(int i) +{ + conv t; + + t.b[0] = i & 0xff; i >>= 8; + t.b[1] = i & 0xff; i >>= 8; + t.b[2] = i & 0xff; i >>= 8; + t.b[3] = i & 0xff; i >>= 8; + return t.i; +} + +short intel_short(short l) +{ + conv t; + + t.b[0] = l & 0xff; l >>= 8; + t.b[1] = l & 0xff; l >>= 8; + return t.s[0]; +} + +void die(const char * str) +{ + fprintf(stderr,"%s\n",str); + exit(1); +} + +void usage(void) +{ + die("Usage: build bootsect setup system [rootdev] [> image]"); +} + +int main(int argc, char ** argv) +{ + int i,c,id,sz,tmp_int; + unsigned long sys_size, tmp_long; + char buf[1024]; +#ifndef __BFD__ + struct exec *ex = (struct exec *)buf; +#endif + char major_root, minor_root; + struct stat sb; + unsigned char setup_sectors; + + if ((argc < 4) || (argc > 5)) + usage(); + if (argc > 4) { + if (!strcmp(argv[4], "CURRENT")) { + if (stat("/", &sb)) { + perror("/"); + die("Couldn't stat /"); + } + major_root = major(sb.st_dev); + minor_root = minor(sb.st_dev); + } else if (strcmp(argv[4], "FLOPPY")) { + if (stat(argv[4], &sb)) { + perror(argv[4]); + die("Couldn't stat root device."); + } + major_root = major(sb.st_rdev); + minor_root = minor(sb.st_rdev); + } else { + major_root = 0; + minor_root = 0; + } + } else { + major_root = DEFAULT_MAJOR_ROOT; + minor_root = DEFAULT_MINOR_ROOT; + } + fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root); + for (i=0;i<sizeof buf; i++) buf[i]=0; + if ((id=open(argv[1],O_RDONLY,0))<0) + die("Unable to open 'boot'"); + if (read(id,buf,MINIX_HEADER) != MINIX_HEADER) + die("Unable to read header of 'boot'"); + if (((long *) buf)[0]!=intel_long(0x04100301)) + die("Non-Minix header of 'boot'"); + if (((long *) buf)[1]!=intel_long(MINIX_HEADER)) + die("Non-Minix header of 'boot'"); + if (((long *) buf)[3] != 0) + die("Illegal data segment in 'boot'"); + if (((long *) buf)[4] != 0) + die("Illegal bss in 'boot'"); + if (((long *) buf)[5] != 0) + die("Non-Minix header of 'boot'"); + if (((long *) buf)[7] != 0) + die("Illegal symbol table in 'boot'"); + i=read(id,buf,sizeof buf); + fprintf(stderr,"Boot sector %d bytes.\n",i); + if (i != 512) + die("Boot block must be exactly 512 bytes"); + if ((*(unsigned short *)(buf+510)) != (unsigned short)intel_short(0xAA55)) + die("Boot block hasn't got boot flag (0xAA55)"); + buf[508] = (char) minor_root; + buf[509] = (char) major_root; + i=write(1,buf,512); + if (i!=512) + die("Write call failed"); + close (id); + + if ((id=open(argv[2],O_RDONLY,0))<0) + die("Unable to open 'setup'"); + if (read(id,buf,MINIX_HEADER) != MINIX_HEADER) + die("Unable to read header of 'setup'"); + if (((long *) buf)[0]!=intel_long(0x04100301)) + die("Non-Minix header of 'setup'"); + if (((long *) buf)[1]!=intel_long(MINIX_HEADER)) + die("Non-Minix header of 'setup'"); + if (((long *) buf)[3] != 0) + die("Illegal data segment in 'setup'"); + if (((long *) buf)[4] != 0) + die("Illegal bss in 'setup'"); + if (((long *) buf)[5] != 0) + die("Non-Minix header of 'setup'"); + if (((long *) buf)[7] != 0) + die("Illegal symbol table in 'setup'"); + for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c ) +#ifdef __BIG_KERNEL__ + { + if (!i) { + /* Working with memcpy because of alignment constraints + on Sparc - Gertjan */ + memcpy(&tmp_long, &buf[2], sizeof(long)); + if (tmp_long != intel_long(0x53726448) ) + die("Wrong magic in loader header of 'setup'"); + memcpy(&tmp_int, &buf[6], sizeof(int)); + if (tmp_int < intel_int(0x200)) + die("Wrong version of loader header of 'setup'"); + buf[0x11] = 1; /* LOADED_HIGH */ + tmp_long = intel_long(0x100000); + memcpy(&buf[0x14], &tmp_long, sizeof(long)); /* code32_start */ + } +#endif + if (write(1,buf,c)!=c) + die("Write call failed"); +#ifdef __BIG_KERNEL__ + } +#endif + if (c != 0) + die("read-error on 'setup'"); + close (id); + setup_sectors = (unsigned char)((i + 511) / 512); + /* for compatibility with LILO */ + if (setup_sectors < SETUP_SECTS) + setup_sectors = SETUP_SECTS; + fprintf(stderr,"Setup is %d bytes.\n",i); + for (c=0 ; c<sizeof(buf) ; c++) + buf[c] = '\0'; + while (i < setup_sectors * 512) { + c = setup_sectors * 512 - i; + if (c > sizeof(buf)) + c = sizeof(buf); + if (write(1,buf,c) != c) + die("Write call failed"); + i += c; + } + + if ((id=open(argv[3],O_RDONLY,0))<0) + die("Unable to open 'system'"); +#ifndef __BFD__ + if (read(id,buf,GCC_HEADER) != GCC_HEADER) + die("Unable to read header of 'system'"); + if (N_MAGIC(*ex) == ZMAGIC) { + GCC_HEADER = N_MAGIC_OFFSET; + lseek(id, GCC_HEADER, SEEK_SET); + } else if (N_MAGIC(*ex) != QMAGIC) + die("Non-GCC header of 'system'"); + fprintf(stderr,"System is %d kB (%d kB code, %d kB data and %d kB bss)\n", + (ex->a_text+ex->a_data+ex->a_bss)/1024, + ex->a_text /1024, + ex->a_data /1024, + ex->a_bss /1024); + sz = N_SYMOFF(*ex) - GCC_HEADER + 4; +#else + if (fstat (id, &sb)) { + perror ("fstat"); + die ("Unable to stat 'system'"); + } + sz = sb.st_size; + fprintf (stderr, "System is %d kB\n", sz/1024); +#endif + sys_size = (sz + 15) / 16; + if (sys_size > SYS_SIZE) + die("System is too big"); + while (sz > 0) { + int l, n; + + l = sz; + if (l > sizeof(buf)) + l = sizeof(buf); + if ((n=read(id, buf, l)) != l) { + if (n == -1) + perror(argv[1]); + else + fprintf(stderr, "Unexpected EOF\n"); + die("Can't read 'system'"); + } + if (write(1, buf, l) != l) + die("Write failed"); + sz -= l; + } + close(id); + if (lseek(1, 497, 0) == 497) { + if (write(1, &setup_sectors, 1) != 1) + die("Write of setup sectors failed"); + } + if (lseek(1,500,0) == 500) { + buf[0] = (sys_size & 0xff); + buf[1] = ((sys_size >> 8) & 0xff); + if (write(1, buf, 2) != 2) + die("Write failed"); + } + return(0); +} diff --git a/arch/cris/arch-v10/defconfig b/arch/cris/arch-v10/defconfig new file mode 100644 index 00000000000..2a3411eaace --- /dev/null +++ b/arch/cris/arch-v10/defconfig @@ -0,0 +1,505 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# General setup +# +CONFIG_NET=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_SYSCTL is not set +CONFIG_BINFMT_ELF=y +# CONFIG_ETRAX_KGDB is not set +# CONFIG_ETRAX_WATCHDOG is not set + +# +# Hardware setup +# +CONFIG_ETRAX100LX=y +# CONFIG_ETRAX100LX_V2 is not set +# CONFIG_SVINTO_SIM is not set +CONFIG_CRIS_LOW_MAP=y +CONFIG_ETRAX_DRAM_VIRTUAL_BASE=60000000 +CONFIG_ETRAX_DRAM_SIZE=8 +CONFIG_ETRAX_FLASH_BUSWIDTH=2 +CONFIG_ETRAX_ROOT_DEVICE="/dev/mtdblock3" +CONFIG_ETRAX_PA_LEDS=y +# CONFIG_ETRAX_PB_LEDS is not set +# CONFIG_ETRAX_CSP0_LEDS is not set +# CONFIG_ETRAX_NO_LEDS is not set +CONFIG_ETRAX_LED1G=2 +CONFIG_ETRAX_LED1R=2 +CONFIG_ETRAX_LED2G=2 +CONFIG_ETRAX_LED2R=2 +CONFIG_ETRAX_LED3R=2 +CONFIG_ETRAX_LED3G=2 +CONFIG_ETRAX_LED4R=2 +CONFIG_ETRAX_LED4G=2 +CONFIG_ETRAX_LED5R=2 +CONFIG_ETRAX_LED5G=2 +CONFIG_ETRAX_LED6R=2 +CONFIG_ETRAX_LED6G=2 +CONFIG_ETRAX_LED7R=2 +CONFIG_ETRAX_LED7G=2 +CONFIG_ETRAX_LED8Y=2 +CONFIG_ETRAX_LED9Y=2 +CONFIG_ETRAX_LED10Y=2 +CONFIG_ETRAX_LED11Y=2 +CONFIG_ETRAX_LED12R=2 +CONFIG_ETRAX_DEBUG_PORT0=y +# CONFIG_ETRAX_DEBUG_PORT1 is not set +# CONFIG_ETRAX_DEBUG_PORT2 is not set +# CONFIG_ETRAX_DEBUG_PORT3 is not set +CONFIG_ETRAX_RESCUE_SER0=y +# CONFIG_ETRAX_RESCUE_SER1 is not set +# CONFIG_ETRAX_RESCUE_SER2 is not set +# CONFIG_ETRAX_RESCUE_SER3 is not set +CONFIG_ETRAX_DEF_R_WAITSTATES=95a6 +CONFIG_ETRAX_DEF_R_BUS_CONFIG=104 +# CONFIG_ETRAX_SDRAM is not set +CONFIG_ETRAX_DEF_R_DRAM_CONFIG=1a200040 +CONFIG_ETRAX_DEF_R_DRAM_TIMING=5611 +CONFIG_ETRAX_DEF_R_PORT_PA_DIR=1d +CONFIG_ETRAX_DEF_R_PORT_PA_DATA=f0 +CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG=00 +CONFIG_ETRAX_DEF_R_PORT_PB_DIR=1e +CONFIG_ETRAX_DEF_R_PORT_PB_DATA=f3 +# CONFIG_ETRAX_SOFT_SHUTDOWN is not set + +# +# Drivers for ETRAX 100LX built-in interfaces +# +CONFIG_ETRAX_ETHERNET=y +CONFIG_NET_ETHERNET=y +# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set +CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y +# CONFIG_ETRAX_ETHERNET_LPSLAVE is not set +CONFIG_ETRAX_SERIAL=y +CONFIG_ETRAX_SERIAL_PORT0=y +# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB is not set +CONFIG_ETRAX_SERIAL_PORT1=y +# CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB is not set +# CONFIG_ETRAX_SERIAL_PORT2 is not set +# CONFIG_ETRAX_SERIAL_PORT3 is not set +# CONFIG_ETRAX_RS485 is not set +# CONFIG_ETRAX_SYNCHRONOUS_SERIAL is not set +# CONFIG_ETRAX_IDE is not set +CONFIG_ETRAX_AXISFLASHMAP=y +CONFIG_ETRAX_PTABLE_SECTOR=65536 +CONFIG_MTD=y +CONFIG_MTD_CFI=y +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_AMDSTD=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_ETRAX_I2C=y +CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C=y +# CONFIG_ETRAX_I2C_EEPROM is not set +CONFIG_ETRAX_GPIO=y +CONFIG_ETRAX_PA_BUTTON_BITMASK=02 +CONFIG_ETRAX_PA_CHANGEABLE_DIR=00 +CONFIG_ETRAX_PA_CHANGEABLE_BITS=FF +CONFIG_ETRAX_PB_CHANGEABLE_DIR=00 +CONFIG_ETRAX_PB_CHANGEABLE_BITS=FF +# CONFIG_ETRAX_USB_HOST is not set +# CONFIG_USB is not set +# CONFIG_ETRAX_DS1302 is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC1000 is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOCPROBE is not set + +# +# RAM/ROM Device Drivers +# +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_MTDRAM is not set + +# +# Linearly Mapped Flash Device Drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_CFI_GEOMETRY is not set +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_AMDSTD=y +# CONFIG_MTD_SHARP is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_NORA is not set +# CONFIG_MTD_PNC2000 is not set +# CONFIG_MTD_RPXLITE is not set +# CONFIG_MTD_SC520CDP is not set +# CONFIG_MTD_SBC_MEDIAGX is not set +# CONFIG_MTD_ELAN_104NC is not set +# CONFIG_MTD_SA1100 is not set +# CONFIG_MTD_DC21285 is not set +# CONFIG_MTD_CSTM_CFI_JEDEC is not set +# CONFIG_MTD_JEDEC is not set +# CONFIG_MTD_MIXMEM is not set +# CONFIG_MTD_OCTAGON is not set +# CONFIG_MTD_VMAX is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_NAND_SPIA is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set +# CONFIG_PHONE_IXJ is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# IDE, ATA and ATAPI Block devices +# +# CONFIG_BLK_DEV_IDE is not set +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +# CONFIG_BLK_DEV_IDEDISK is not set +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_IDE_MODES is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Input core support +# +# CONFIG_INPUT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_UNIX98_PTYS is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_CRAMFS=y +CONFIG_RAMFS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_DEBUG is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_EXT2_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_V3 is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_MOUNT_SUBDIR is not set +# CONFIG_NCPFS_NDS_DOMAINS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Kernel hacking +# +# CONFIG_PROFILE is not set diff --git a/arch/cris/arch-v10/drivers/Kconfig b/arch/cris/arch-v10/drivers/Kconfig new file mode 100644 index 00000000000..748374f25b8 --- /dev/null +++ b/arch/cris/arch-v10/drivers/Kconfig @@ -0,0 +1,963 @@ +config ETRAX_ETHERNET + bool "Ethernet support" + depends on ETRAX_ARCH_V10 + help + This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet + controller. + +# this is just so that the user does not have to go into the +# normal ethernet driver section just to enable ethernetworking +config NET_ETHERNET + bool + depends on ETRAX_ETHERNET + default y + +choice + prompt "Network LED behavior" + depends on ETRAX_ETHERNET + default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY + +config ETRAX_NETWORK_LED_ON_WHEN_LINK + bool "LED_on_when_link" + help + Selecting LED_on_when_link will light the LED when there is a + connection and will flash off when there is activity. + + Selecting LED_on_when_activity will light the LED only when + there is activity. + + This setting will also affect the behaviour of other activity LEDs + e.g. Bluetooth. + +config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY + bool "LED_on_when_activity" + help + Selecting LED_on_when_link will light the LED when there is a + connection and will flash off when there is activity. + + Selecting LED_on_when_activity will light the LED only when + there is activity. + + This setting will also affect the behaviour of other activity LEDs + e.g. Bluetooth. + +endchoice + +config 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 DMA out assignment" + depends on ETRAX_SERIAL_PORT0 + default ETRAX_SERIAL_PORT0_DMA6_OUT + +config CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_OUT + bool "No DMA out" + +config CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT + bool "DMA 6" + +endchoice + +choice + prompt "Ser0 DMA in assignment" + depends on ETRAX_SERIAL_PORT0 + default ETRAX_SERIAL_PORT0_DMA7_IN + +config CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN + bool "No DMA in" + +config CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN + bool "DMA 7" + +endchoice + +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 DMA out assignment" + depends on ETRAX_SERIAL_PORT1 + default ETRAX_SERIAL_PORT1_DMA8_OUT + +config CONFIG_ETRAX_SERIAL_PORT1_NO_DMA_OUT + bool "No DMA out" + +config CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT + bool "DMA 8" + +endchoice + +choice + prompt "Ser1 DMA in assignment" + depends on ETRAX_SERIAL_PORT1 + default ETRAX_SERIAL_PORT1_DMA9_IN + +config CONFIG_ETRAX_SERIAL_PORT1_NO_DMA_IN + bool "No DMA in" + +config CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN + bool "DMA 9" + +endchoice + +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 dont 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 DMA out assignment" + depends on ETRAX_SERIAL_PORT2 + default ETRAX_SERIAL_PORT2_DMA2_OUT + +config CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_OUT + bool "No DMA out" + +config CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT + bool "DMA 2" + +endchoice + +choice + prompt "Ser2 DMA in assignment" + depends on ETRAX_SERIAL_PORT2 + default ETRAX_SERIAL_PORT2_DMA3_IN + +config CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_IN + bool "No DMA in" + +config CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN + bool "DMA 3" + +endchoice + +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 DMA out assignment" + depends on ETRAX_SERIAL_PORT3 + default ETRAX_SERIAL_PORT3_DMA4_OUT + +config CONFIG_ETRAX_SERIAL_PORT3_NO_DMA_OUT + bool "No DMA out" + +config CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT + bool "DMA 4" + +endchoice + +choice + prompt "Ser3 DMA in assignment" + depends on ETRAX_SERIAL_PORT3 + default ETRAX_SERIAL_PORT3_DMA5_IN + +config CONFIG_ETRAX_SERIAL_PORT3_NO_DMA_IN + bool "No DMA in" + +config CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN + bool "DMA 5" + +endchoice + +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://www.hw.cz/english/docs/rs485/rs485.html>. + +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_IDE + bool "ATA/IDE support" + select IDE + select BLK_DEV_IDE + select BLK_DEV_IDEDISK + select BLK_DEV_IDECD + select BLK_DEV_IDEDMA + select DMA_NONPCI + help + Enable this to get support for ATA/IDE. + You can't use paralell ports or SCSI ports + at the same time. + + +config ETRAX_IDE_DELAY + int "Delay for drives to regain consciousness" + depends on ETRAX_IDE + default 15 + help + Number of seconds to wait for IDE drives to spin up after an IDE + reset. +choice + prompt "IDE reset pin" + depends on ETRAX_IDE + default ETRAX_IDE_PB7_RESET + +config ETRAX_IDE_PB7_RESET + bool "Port_PB_Bit_7" + help + IDE reset on pin 7 on port B + +config ETRAX_IDE_G27_RESET + bool "Port_G_Bit_27" + help + IDE reset on pin 27 on port G + +endchoice + + +config ETRAX_USB_HOST + bool "USB host" + help + This option enables the host functionality of the ETRAX 100LX + built-in USB controller. In host mode the controller is designed + for CTRL and BULK traffic only, INTR traffic may work as well + however (depending on the requirements of timeliness). + +config USB + tristate + depends on ETRAX_USB_HOST + default y + +config ETRAX_USB_HOST_PORT1 + bool " USB port 1 enabled" + depends on ETRAX_USB_HOST + default n + +config ETRAX_USB_HOST_PORT2 + bool " USB port 2 enabled" + depends on ETRAX_USB_HOST + default n + +config ETRAX_AXISFLASHMAP + bool "Axis flash-map support" + depends on ETRAX_ARCH_V10 + help + This option enables MTD mapping of flash devices. Needed to use + flash memories. If unsure, say Y. + +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). + +# here we define the CONFIG_'s necessary to enable MTD support +# for the flash +config MTD + tristate + depends on ETRAX_AXISFLASHMAP + default y + help + Memory Technology Devices are flash, RAM and similar chips, often + used for solid state file systems on embedded devices. This option + will provide the generic support for MTD drivers to register + themselves with the kernel and for potential users of MTD devices + to enumerate the devices which are present and obtain a handle on + them. It will also allow you to select individual drivers for + particular hardware and users of MTD devices. If unsure, say N. + +config MTD_CFI + tristate + depends on ETRAX_AXISFLASHMAP + default y + help + The Common Flash Interface specification was developed by Intel, + AMD and other flash manufactures that provides a universal method + for probing the capabilities of flash devices. If you wish to + support any device that is CFI-compliant, you need to enable this + option. Visit <http://www.amd.com/products/nvd/overview/cfi.html> + for more information on CFI. + +config MTD_CFI_AMDSTD + tristate + depends on ETRAX_AXISFLASHMAP + default y + help + The Common Flash Interface defines a number of different command + sets which a CFI-compliant chip may claim to implement. This code + provides support for one of those command sets, used on chips + chips including the AMD Am29LV320. + +config MTD_OBSOLETE_CHIPS + bool + depends on ETRAX_AXISFLASHMAP + default y + help + This option does not enable any code directly, but will allow you to + select some other chip drivers which are now considered obsolete, + because the generic CONFIG_JEDEC_PROBE code above should now detect + the chips which are supported by these drivers, and allow the generic + CFI-compatible drivers to drive the chips. Say 'N' here unless you have + already tried the CONFIG_JEDEC_PROBE method and reported its failure + to the MTD mailing list at <linux-mtd@lists.infradead.org> + +config MTD_AMDSTD + tristate + depends on ETRAX_AXISFLASHMAP + default y + help + This option enables support for flash chips using AMD-compatible + commands, including some which are not CFI-compatible and hence + cannot be used with the CONFIG_MTD_CFI_AMDSTD option. + + It also works on AMD compatible chips that do conform to CFI. + +config MTD_CHAR + tristate + depends on ETRAX_AXISFLASHMAP + default y + help + This provides a character device for each MTD device present in + the system, allowing the user to read and write directly to the + memory chips, and also use ioctl() to obtain information about + the device, or to erase parts of it. + +config MTD_BLOCK + tristate + depends on ETRAX_AXISFLASHMAP + default y + ---help--- + Although most flash chips have an erase size too large to be useful + as block devices, it is possible to use MTD devices which are based + on RAM chips in this manner. This block device is a user of MTD + devices performing that function. + + At the moment, it is also required for the Journalling Flash File + System(s) to obtain a handle on the MTD device when it's mounted + (although JFFS and JFFS2 don't actually use any of the functionality + of the mtdblock device). + + Later, it may be extended to perform read/erase/modify/write cycles + on flash chips to emulate a smaller block size. Needless to say, + this is very unsafe, but could be useful for file systems which are + almost never written to. + + You do not need this option for use with the DiskOnChip devices. For + those, enable NFTL support (CONFIG_NFTL) instead. + +config MTD_PARTITIONS + tristate + depends on ETRAX_AXISFLASHMAP + default y + help + If you have a device which needs to divide its flash chip(s) up + into multiple 'partitions', each of which appears to the user as + a separate MTD device, you require this option to be enabled. If + unsure, say 'Y'. + + Note, however, that you don't need this option for the DiskOnChip + devices. Partitioning on NFTL 'devices' is a different - that's the + 'normal' form of partitioning used on a block device. + +config MTD_CONCAT + tristate + depends on ETRAX_AXISFLASHMAP + default y + +config ETRAX_I2C + bool "I2C support" + depends on ETRAX_ARCH_V10 + 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 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_RTC + bool "Real Time Clock support" + depends on ETRAX_ARCH_V10 + help + Enables drivers for the Real-Time Clock battery-backed chips on + some products. The kernel reads the time when booting, and + the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a + rtc_time struct (see <file:include/asm-cris/rtc.h>) on the /dev/rtc + device, major 121. You can check the time with cat /proc/rtc, but + normal time reading should be done using libc function time and + friends. + +choice + prompt "RTC chip" + depends on ETRAX_RTC + default ETRAX_DS1302 + +config ETRAX_DS1302 + bool "DS1302" + help + Enables the driver for the DS1302 Real-Time Clock battery-backed + chip on some products. + +config ETRAX_PCF8563 + bool "PCF8563" + help + Enables the driver for the PCF8563 Real-Time Clock battery-backed + chip on some products. + +endchoice + +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 rechargable 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 + + diff --git a/arch/cris/arch-v10/drivers/Makefile b/arch/cris/arch-v10/drivers/Makefile new file mode 100644 index 00000000000..20258e36f38 --- /dev/null +++ b/arch/cris/arch-v10/drivers/Makefile @@ -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 + + diff --git a/arch/cris/arch-v10/drivers/axisflashmap.c b/arch/cris/arch-v10/drivers/axisflashmap.c new file mode 100644 index 00000000000..fb7d4855ea6 --- /dev/null +++ b/arch/cris/arch-v10/drivers/axisflashmap.c @@ -0,0 +1,541 @@ +/* + * 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. + * + * $Log: axisflashmap.c,v $ + * Revision 1.10 2004/08/16 12:37:22 starvik + * Merge of Linux 2.6.8 + * + * Revision 1.8 2004/05/14 07:58:03 starvik + * Merge of changes from 2.4 + * + * Revision 1.6 2003/07/04 08:27:37 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.5 2002/12/11 13:13:57 starvik + * Added arch/ to v10 specific includes + * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) + * + * Revision 1.4 2002/11/20 11:56:10 starvik + * Merge of Linux 2.5.48 + * + * Revision 1.3 2002/11/13 14:54:13 starvik + * Copied from linux 2.4 + * + * Revision 1.28 2002/10/01 08:08:43 jonashg + * The first partition ends at the start of the partition table. + * + * Revision 1.27 2002/08/21 09:23:13 jonashg + * Speling. + * + * Revision 1.26 2002/08/21 08:35:20 jonashg + * Cosmetic change to printouts. + * + * Revision 1.25 2002/08/21 08:15:42 jonashg + * Made it compile even without CONFIG_MTD_CONCAT defined. + * + * Revision 1.24 2002/08/20 13:12:35 jonashg + * * New approach to probing. Probe cse0 and cse1 separately and (mtd)concat + * the results. + * * Removed compile time tests concerning how the mtdram driver has been + * configured. The user will know about the misconfiguration at runtime + * instead. (The old approach made it impossible to use mtdram for anything + * else than RAM boot). + * + * Revision 1.23 2002/05/13 12:12:28 johana + * Allow compile without CONFIG_MTD_MTDRAM but warn at compiletime and + * be informative at runtime. + * + * Revision 1.22 2002/05/13 10:24:44 johana + * Added #if checks on MTDRAM CONFIG + * + * Revision 1.21 2002/05/06 16:05:20 johana + * Removed debug printout. + * + * Revision 1.20 2002/05/06 16:03:00 johana + * No more cramfs as root hack in generic code. + * It's handled by axisflashmap using mtdram. + * + * Revision 1.19 2002/03/15 17:10:28 bjornw + * Changed comment about cached access since we changed this before + * + * Revision 1.18 2002/03/05 17:06:15 jonashg + * Try amd_flash probe before cfi_probe since amd_flash driver can handle two + * (or more) flash chips of different model and the cfi driver cannot. + * + * Revision 1.17 2001/11/12 19:42:38 pkj + * Fixed compiler warnings. + * + * Revision 1.16 2001/11/08 11:18:58 jonashg + * Always read from uncached address to avoid problems with flushing + * cachelines after write and MTD-erase. No performance loss have been + * seen yet. + * + * Revision 1.15 2001/10/19 12:41:04 jonashg + * Name of probe has changed in MTD. + * + * Revision 1.14 2001/09/21 07:14:10 jonashg + * Made root filesystem (cramfs) use mtdblock driver when booting from flash. + * + * Revision 1.13 2001/08/15 13:57:35 jonashg + * Entire MTD updated to the linux 2.4.7 version. + * + * Revision 1.12 2001/06/11 09:50:30 jonashg + * Oops, 2MB is 0x200000 bytes. + * + * Revision 1.11 2001/06/08 11:39:44 jonashg + * Changed sizes and offsets in axis_default_partitions to use + * CONFIG_ETRAX_PTABLE_SECTOR. + * + * Revision 1.10 2001/05/29 09:42:03 jonashg + * Use macro for end marker length instead of sizeof. + * + * Revision 1.9 2001/05/29 08:52:52 jonashg + * Gave names to the magic fours (size of the ptable end marker). + * + * Revision 1.8 2001/05/28 15:36:20 jonashg + * * Removed old comment about ptable location in flash (it's a CONFIG_ option). + * * Variable ptable was initialized twice to the same value. + * + * Revision 1.7 2001/04/05 13:41:46 markusl + * Updated according to review remarks + * + * Revision 1.6 2001/03/07 09:21:21 bjornw + * No need to waste .data + * + * Revision 1.5 2001/03/06 16:27:01 jonashg + * Probe the entire flash area for flash devices. + * + * Revision 1.4 2001/02/23 12:47:15 bjornw + * Uncached flash in LOW_MAP moved from 0xe to 0x8 + * + * Revision 1.3 2001/02/16 12:11:45 jonashg + * MTD driver amd_flash is now included in MTD CVS repository. + * (It's now in drivers/mtd). + * + * Revision 1.2 2001/02/09 11:12:22 jonashg + * Support for AMD compatible non-CFI flash chips. + * Only tested with Toshiba TC58FVT160 so far. + * + * Revision 1.1 2001/01/12 17:01:18 bjornw + * * Added axisflashmap.c, a physical mapping for MTD that reads and understands + * Axis partition-table format. + * + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/config.h> +#include <linux/init.h> + +#include <linux/mtd/concat.h> +#include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/mtdram.h> +#include <linux/mtd/partitions.h> + +#include <asm/axisflashmap.h> +#include <asm/mmu.h> +#include <asm/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 __u16 +#endif + +/* From head.S */ +extern unsigned long romfs_start, romfs_length, romfs_in_flash; + +/* The master mtd for the entire flash. */ +struct mtd_info* axisflash_mtd = NULL; + +/* Map driver functions. */ + +static map_word flash_read(struct map_info *map, unsigned long ofs) +{ + map_word tmp; + tmp.x[0] = *(flash_data *)(map->map_priv_1 + ofs); + return tmp; +} + +static void flash_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + memcpy(to, (void *)(map->map_priv_1 + from), len); +} + +static void flash_write(struct map_info *map, map_word d, unsigned long adr) +{ + *(flash_data *)(map->map_priv_1 + adr) = (flash_data)d.x[0]; +} + +/* + * The map for chip select e0. + * + * We run into tricky coherence situations if we mix cached with uncached + * accesses to we only use the uncached version here. + * + * The size field is the total size where the flash chips may be mapped on the + * chip select. MTD probes should find all devices there and it does not matter + * if there are unmapped gaps or aliases (mirrors of flash devices). The MTD + * probes will ignore them. + * + * The start address in map_priv_1 is in virtual memory so we cannot use + * MEM_CSE0_START but must rely on that FLASH_UNCACHED_ADDR is the start + * address of cse0. + */ +static struct map_info map_cse0 = { + .name = "cse0", + .size = MEM_CSE0_SIZE, + .bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH, + .read = flash_read, + .copy_from = flash_copy_from, + .write = flash_write, + .map_priv_1 = FLASH_UNCACHED_ADDR +}; + +/* + * The map for chip select e1. + * + * If there was a gap between cse0 and cse1, map_priv_1 would get the wrong + * address, but there isn't. + */ +static struct map_info map_cse1 = { + .name = "cse1", + .size = MEM_CSE1_SIZE, + .bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH, + .read = flash_read, + .copy_from = flash_copy_from, + .write = flash_write, + .map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE +}; + +/* If no partition-table was found, we use this default-set. */ +#define MAX_PARTITIONS 7 +#define NUM_DEFAULT_PARTITIONS 3 + +/* + * Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the + * size of one flash block and "filesystem"-partition needs 5 blocks to be able + * to use JFFS. + */ +static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { + { + .name = "boot firmware", + .size = CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0 + }, + { + .name = "kernel", + .size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR), + .offset = CONFIG_ETRAX_PTABLE_SECTOR + }, + { + .name = "filesystem", + .size = 5 * CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR) + } +}; + +/* Initialize the ones normally used. */ +static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { + { + .name = "part0", + .size = CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0 + }, + { + .name = "part1", + .size = 0, + .offset = 0 + }, + { + .name = "part2", + .size = 0, + .offset = 0 + }, + { + .name = "part3", + .size = 0, + .offset = 0 + }, + { + .name = "part4", + .size = 0, + .offset = 0 + }, + { + .name = "part5", + .size = 0, + .offset = 0 + }, + { + .name = "part6", + .size = 0, + .offset = 0 + }, +}; + +/* + * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash + * chips in that order (because the amd_flash-driver is faster). + */ +static struct mtd_info *probe_cs(struct map_info *map_cs) +{ + struct mtd_info *mtd_cs = NULL; + + printk(KERN_INFO + "%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n", + map_cs->name, map_cs->size, map_cs->map_priv_1); + +#ifdef CONFIG_MTD_AMDSTD + mtd_cs = do_map_probe("amd_flash", map_cs); +#endif +#ifdef CONFIG_MTD_CFI + if (!mtd_cs) { + mtd_cs = do_map_probe("cfi_probe", map_cs); + } +#endif + + return mtd_cs; +} + +/* + * Probe each chip select individually for flash chips. If there are chips on + * both cse0 and cse1, the mtd_info structs will be concatenated to one struct + * so that MTD partitions can cross chip boundries. + * + * The only known restriction to how you can mount your chips is that each + * chip select must hold similar flash chips. But you need external hardware + * to do that anyway and you can put totally different chips on cse0 and cse1 + * so it isn't really much of a restriction. + */ +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) { +#ifdef CONFIG_MTD_CONCAT + 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, + sizeof(mtds) / sizeof(mtds[0]), + "cse0+cse1"); +#else + printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel " + "(mis)configuration!\n", map_cse0.name, map_cse1.name); + mtd_cse = NULL; +#endif + if (!mtd_cse) { + printk(KERN_ERR "%s and %s: Concatenation failed!\n", + map_cse0.name, map_cse1.name); + + /* The best we can do now is to only use what we found + * at cse0. + */ + mtd_cse = mtd_cse0; + map_destroy(mtd_cse1); + } + } else { + mtd_cse = mtd_cse0? mtd_cse0 : mtd_cse1; + } + + return mtd_cse; +} + +/* + * Probe the flash chip(s) and, if it succeeds, read the partition-table + * and register the partitions with MTD. + */ +static int __init init_axis_flash(void) +{ + struct mtd_info *mymtd; + int err = 0; + int pidx = 0; + struct partitiontable_head *ptable_head = NULL; + struct partitiontable_entry *ptable; + int use_default_ptable = 1; /* Until proven otherwise. */ + const char *pmsg = " /dev/flash%d at 0x%08x, size 0x%08x\n"; + + if (!(mymtd = flash_probe())) { + /* There's no reason to use this module if no flash chip can + * be identified. Make sure that's understood. + */ + printk(KERN_INFO "axisflashmap: Found no flash chip.\n"); + } else { + printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n", + mymtd->name, mymtd->size); + axisflash_mtd = mymtd; + } + + if (mymtd) { + mymtd->owner = THIS_MODULE; + ptable_head = (struct partitiontable_head *)(FLASH_CACHED_ADDR + + CONFIG_ETRAX_PTABLE_SECTOR + + PARTITION_TABLE_OFFSET); + } + pidx++; /* First partition is always set to the default. */ + + if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC) + && (ptable_head->size < + (MAX_PARTITIONS * sizeof(struct partitiontable_entry) + + PARTITIONTABLE_END_MARKER_SIZE)) + && (*(unsigned long*)((void*)ptable_head + sizeof(*ptable_head) + + ptable_head->size - + PARTITIONTABLE_END_MARKER_SIZE) + == PARTITIONTABLE_END_MARKER)) { + /* Looks like a start, sane length and end of a + * partition table, lets check csum etc. + */ + int ptable_ok = 0; + struct partitiontable_entry *max_addr = + (struct partitiontable_entry *) + ((unsigned long)ptable_head + sizeof(*ptable_head) + + ptable_head->size); + unsigned long offset = CONFIG_ETRAX_PTABLE_SECTOR; + unsigned char *p; + unsigned long csum = 0; + + ptable = (struct partitiontable_entry *) + ((unsigned long)ptable_head + sizeof(*ptable_head)); + + /* Lets be PARANOID, and check the checksum. */ + p = (unsigned char*) ptable; + + while (p <= (unsigned char*)max_addr) { + csum += *p++; + csum += *p++; + csum += *p++; + csum += *p++; + } + ptable_ok = (csum == ptable_head->checksum); + + /* Read the entries and use/show the info. */ + printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n", + (ptable_ok ? " valid" : "n invalid"), ptable_head, + max_addr); + + /* We have found a working bootblock. Now read the + * partition table. Scan the table. It ends when + * there is 0xffffffff, that is, empty flash. + */ + while (ptable_ok + && ptable->offset != 0xffffffff + && ptable < max_addr + && pidx < MAX_PARTITIONS) { + + axis_partitions[pidx].offset = offset + ptable->offset; + axis_partitions[pidx].size = ptable->size; + + printk(pmsg, pidx, axis_partitions[pidx].offset, + axis_partitions[pidx].size); + pidx++; + ptable++; + } + use_default_ptable = !ptable_ok; + } + + if (romfs_in_flash) { + /* Add an overlapping device for the root partition (romfs). */ + + axis_partitions[pidx].name = "romfs"; + axis_partitions[pidx].size = romfs_length; + axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; + axis_partitions[pidx].mask_flags |= MTD_WRITEABLE; + + printk(KERN_INFO + " Adding readonly flash partition for romfs image:\n"); + printk(pmsg, pidx, axis_partitions[pidx].offset, + axis_partitions[pidx].size); + pidx++; + } + + if (mymtd) { + if (use_default_ptable) { + printk(KERN_INFO " Using default partition table.\n"); + err = add_mtd_partitions(mymtd, axis_default_partitions, + NUM_DEFAULT_PARTITIONS); + } else { + err = add_mtd_partitions(mymtd, axis_partitions, pidx); + } + + if (err) { + panic("axisflashmap could not add MTD partitions!\n"); + } + } + + if (!romfs_in_flash) { + /* Create an RAM device for the root partition (romfs). */ + +#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0) + /* No use trying to boot this kernel from RAM. Panic! */ + printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM " + "device due to kernel (mis)configuration!\n"); + panic("This kernel cannot boot from RAM!\n"); +#else + struct mtd_info *mtd_ram; + + mtd_ram = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), + GFP_KERNEL); + if (!mtd_ram) { + panic("axisflashmap couldn't allocate memory for " + "mtd_info!\n"); + } + + printk(KERN_INFO " Adding RAM partition for romfs image:\n"); + printk(pmsg, pidx, romfs_start, romfs_length); + + err = mtdram_init_device(mtd_ram, (void*)romfs_start, + romfs_length, "romfs"); + if (err) { + panic("axisflashmap could not initialize MTD RAM " + "device!\n"); + } +#endif + } + + return err; +} + +/* This adds the above to the kernels init-call chain. */ +module_init(init_axis_flash); + +EXPORT_SYMBOL(axisflash_mtd); diff --git a/arch/cris/arch-v10/drivers/ds1302.c b/arch/cris/arch-v10/drivers/ds1302.c new file mode 100644 index 00000000000..fba530fcfae --- /dev/null +++ b/arch/cris/arch-v10/drivers/ds1302.c @@ -0,0 +1,602 @@ +/*!*************************************************************************** +*! +*! FILE NAME : ds1302.c +*! +*! DESCRIPTION: Implements an interface for the DS1302 RTC through Etrax I/O +*! +*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init +*! +*! $Log: ds1302.c,v $ +*! Revision 1.14 2004/08/24 06:48:43 starvik +*! Whitespace cleanup +*! +*! Revision 1.13 2004/05/28 09:26:59 starvik +*! Modified I2C initialization to work in 2.6. +*! +*! Revision 1.12 2004/05/14 07:58:03 starvik +*! Merge of changes from 2.4 +*! +*! Revision 1.10 2004/02/04 09:25:12 starvik +*! Merge of Linux 2.6.2 +*! +*! Revision 1.9 2003/07/04 08:27:37 starvik +*! Merge of Linux 2.5.74 +*! +*! Revision 1.8 2003/04/09 05:20:47 starvik +*! Merge of Linux 2.5.67 +*! +*! Revision 1.6 2003/01/09 14:42:51 starvik +*! Merge of Linux 2.5.55 +*! +*! Revision 1.4 2002/12/11 13:13:57 starvik +*! Added arch/ to v10 specific includes +*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) +*! +*! Revision 1.3 2002/11/20 11:56:10 starvik +*! Merge of Linux 2.5.48 +*! +*! Revision 1.2 2002/11/18 13:16:06 starvik +*! Linux 2.5 port of latest 2.4 drivers +*! +*! Revision 1.15 2002/10/11 16:14:33 johana +*! Added CONFIG_ETRAX_DS1302_TRICKLE_CHARGE and initial setting of the +*! trcklecharge register. +*! +*! Revision 1.14 2002/10/10 12:15:38 magnusmn +*! Added support for having the RST signal on bit g0 +*! +*! Revision 1.13 2002/05/29 15:16:08 johana +*! Removed unused variables. +*! +*! Revision 1.12 2002/04/10 15:35:25 johana +*! Moved probe function closer to init function and marked it __init. +*! +*! Revision 1.11 2001/06/14 12:35:52 jonashg +*! The ATA hack is back. It is unfortunately the only way to set g27 to output. +*! +*! Revision 1.9 2001/06/14 10:00:14 jonashg +*! No need for tempudelay to be inline anymore (had to adjust the usec to +*! loops conversion because of this to make it slow enough to be a udelay). +*! +*! Revision 1.8 2001/06/14 08:06:32 jonashg +*! Made tempudelay delay usecs (well, just a tad more). +*! +*! Revision 1.7 2001/06/13 14:18:11 jonashg +*! Only allow processes with SYS_TIME capability to set time and charge. +*! +*! Revision 1.6 2001/06/12 15:22:07 jonashg +*! * Made init function __init. +*! * Parameter to out_byte() is unsigned char. +*! * The magic number 42 has got a name. +*! * Removed comment about /proc (nothing is exported there). +*! +*! Revision 1.5 2001/06/12 14:35:13 jonashg +*! Gave the module a name and added it to printk's. +*! +*! Revision 1.4 2001/05/31 14:53:40 jonashg +*! Made tempudelay() inline so that the watchdog doesn't reset (see +*! function comment). +*! +*! Revision 1.3 2001/03/26 16:03:06 bjornw +*! Needs linux/config.h +*! +*! Revision 1.2 2001/03/20 19:42:00 bjornw +*! Use the ETRAX prefix on the DS1302 options +*! +*! Revision 1.1 2001/03/20 09:13:50 magnusmn +*! Linux 2.4 port +*! +*! Revision 1.10 2000/07/05 15:38:23 bjornw +*! Dont update kernel time when a RTC_SET_TIME is done +*! +*! Revision 1.9 2000/03/02 15:42:59 macce +*! * Hack to make RTC work on all 2100/2400 +*! +*! Revision 1.8 2000/02/23 16:59:18 torbjore +*! added setup of R_GEN_CONFIG when RTC is connected to the generic port. +*! +*! Revision 1.7 2000/01/17 15:51:43 johana +*! Added RTC_SET_CHARGE ioctl to enable trickle charger. +*! +*! Revision 1.6 1999/10/27 13:19:47 bjornw +*! Added update_xtime_from_cmos which reads back the updated RTC into the kernel. +*! /dev/rtc calls it now. +*! +*! Revision 1.5 1999/10/27 12:39:37 bjornw +*! Disabled superuser check. Anyone can now set the time. +*! +*! Revision 1.4 1999/09/02 13:27:46 pkj +*! Added shadow for R_PORT_PB_CONFIG. +*! Renamed port_g_shadow to port_g_data_shadow. +*! +*! Revision 1.3 1999/09/02 08:28:06 pkj +*! Made it possible to select either port PB or the generic port for the RST +*! signal line to the DS1302 RTC. +*! Also make sure the RST bit is configured as output on Port PB (if used). +*! +*! Revision 1.2 1999/09/01 14:47:20 bjornw +*! Added support for /dev/rtc operations with ioctl RD_TIME and SET_TIME to read +*! and set the date. Register as major 121. +*! +*! Revision 1.1 1999/09/01 09:45:29 bjornw +*! Implemented a DS1302 RTC driver. +*! +*! +*! --------------------------------------------------------------------------- +*! +*! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN +*! +*! $Id: ds1302.c,v 1.14 2004/08/24 06:48:43 starvik Exp $ +*! +*!***************************************************************************/ + +#include <linux/config.h> + +#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/bcd.h> + +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/arch/svinto.h> +#include <asm/io.h> +#include <asm/rtc.h> + +#define RTC_MAJOR_NR 121 /* local major, change later */ + +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); + local_irq_disable(); + + 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); + + BCD_TO_BIN(rtc_tm->tm_sec); + BCD_TO_BIN(rtc_tm->tm_min); + BCD_TO_BIN(rtc_tm->tm_hour); + BCD_TO_BIN(rtc_tm->tm_mday); + BCD_TO_BIN(rtc_tm->tm_mon); + BCD_TO_BIN(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 inode *inode, 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) */ + + BIN_TO_BCD(sec); + BIN_TO_BCD(min); + BIN_TO_BCD(hrs); + BIN_TO_BCD(day); + BIN_TO_BCD(mon); + BIN_TO_BCD(yrs); + + local_irq_save(flags); + local_irq_disable(); + CMOS_WRITE(yrs, RTC_YEAR); + CMOS_WRITE(mon, RTC_MONTH); + CMOS_WRITE(day, RTC_DAY_OF_MONTH); + 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_VLOW_RD: + { + /* TODO: + * Implement voltage low detection support + */ + printk(KERN_WARNING "DS1302: RTC Voltage Low detection" + " is not supported\n"); + return 0; + } + case RTC_VLOW_SET: + { + /* TODO: + * Nothing to do since Voltage Low detection is not supported + */ + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +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 struct file_operations rtc_fops = { + .owner = THIS_MODULE, + .ioctl = rtc_ioctl, +}; + +/* 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) +{ + i2c_init(); + + 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. + */ + genconfig_shadow = ((genconfig_shadow & + ~IO_MASK(R_GEN_CONFIG, ata)) | + (IO_STATE(R_GEN_CONFIG, ata, select))); + *R_GEN_CONFIG = genconfig_shadow; +#elif CONFIG_ETRAX_DS1302_RSTBIT == 0 + + /* Set the direction of this bit to out. */ + genconfig_shadow = ((genconfig_shadow & + ~IO_MASK(R_GEN_CONFIG, g0dir)) | + (IO_STATE(R_GEN_CONFIG, g0dir, out))); + *R_GEN_CONFIG = genconfig_shadow; +#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); diff --git a/arch/cris/arch-v10/drivers/eeprom.c b/arch/cris/arch-v10/drivers/eeprom.c new file mode 100644 index 00000000000..316ca15d680 --- /dev/null +++ b/arch/cris/arch-v10/drivers/eeprom.c @@ -0,0 +1,945 @@ +/*!***************************************************************************** +*! +*! Implements an interface for i2c compatible eeproms to run under linux. +*! Supports 2k, 8k(?) and 16k. Uses adaptive timing adjustents 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. +*! +*! $Log: eeprom.c,v $ +*! Revision 1.10 2003/09/11 07:29:48 starvik +*! Merge of Linux 2.6.0-test5 +*! +*! Revision 1.9 2003/07/04 08:27:37 starvik +*! Merge of Linux 2.5.74 +*! +*! Revision 1.8 2003/04/09 05:20:47 starvik +*! Merge of Linux 2.5.67 +*! +*! Revision 1.6 2003/02/10 07:19:28 starvik +*! Removed misplaced ; +*! +*! Revision 1.5 2002/12/11 13:13:57 starvik +*! Added arch/ to v10 specific includes +*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) +*! +*! Revision 1.4 2002/11/20 11:56:10 starvik +*! Merge of Linux 2.5.48 +*! +*! Revision 1.3 2002/11/18 13:16:06 starvik +*! Linux 2.5 port of latest 2.4 drivers +*! +*! Revision 1.8 2001/06/15 13:24:29 jonashg +*! * Added verification of pointers from userspace in read and write. +*! * Made busy counter volatile. +*! * Added define for inital write delay. +*! * Removed warnings by using loff_t instead of unsigned long. +*! +*! Revision 1.7 2001/06/14 15:26:54 jonashg +*! Removed test because condition is always true. +*! +*! Revision 1.6 2001/06/14 15:18:20 jonashg +*! Kb -> kB (makes quite a difference if you don't know if you have 2k or 16k). +*! +*! Revision 1.5 2001/06/14 14:39:51 jonashg +*! Forgot to use name when registering the driver. +*! +*! Revision 1.4 2001/06/14 14:35:47 jonashg +*! * Gave driver a name and used it in printk's. +*! * Cleanup. +*! +*! Revision 1.3 2001/03/19 16:04:46 markusl +*! Fixed init of fops struct +*! +*! Revision 1.2 2001/03/19 10:35:07 markusl +*! 2.4 port of eeprom driver +*! +*! Revision 1.8 2000/05/18 10:42:25 edgar +*! Make sure to end write cycle on _every_ write +*! +*! Revision 1.7 2000/01/17 17:41:01 johana +*! Adjusted probing and return -ENOSPC when writing outside EEPROM +*! +*! Revision 1.6 2000/01/17 15:50:36 johana +*! Added adaptive timing adjustments and fixed autoprobing for 2k and 16k(?) +*! EEPROMs +*! +*! Revision 1.5 1999/09/03 15:07:37 edgar +*! Added bail-out check to the spinlock +*! +*! Revision 1.4 1999/09/03 12:11:17 bjornw +*! Proper atomicity (need to use spinlocks, not if's). users -> busy. +*! +*! +*! (c) 1999 Axis Communications AB, Lund, Sweden +*!*****************************************************************************/ + +#include <linux/config.h> +#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 <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 */ + wait_queue_head_t wait_q; + volatile int busy; + 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. */ +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) +{ + init_waitqueue_head(&eeprom.wait_q); + eeprom.busy = 0; + +#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(MINOR(inode->i_rdev) != EEPROM_MINOR_NR) + return -ENXIO; + if(MAJOR(inode->i_rdev) != 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) +{ + struct file f; + + f.f_pos = addr; + return eeprom_read(&f, 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 = file->f_pos; + + unsigned char page; + + if(p >= eeprom.size) /* Address i 0 - (size-1) */ + { + return -EFAULT; + } + + while(eeprom.busy) + { + interruptible_sleep_on(&eeprom.wait_q); + + /* bail out if we get interrupted */ + if (signal_pending(current)) + return -EINTR; + + } + eeprom.busy++; + + 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 */ + eeprom.busy--; + wake_up_interruptible(&eeprom.wait_q); + 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) + { + file->f_pos += read; + } + + eeprom.busy--; + wake_up_interruptible(&eeprom.wait_q); + return read; +} + +/* Writes data to eeprom. */ + +static int eeprom_write_buf(loff_t addr, const char * buf, int count) +{ + struct file f; + + f.f_pos = addr; + + return eeprom_write(&f, 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; + } + + while(eeprom.busy) + { + interruptible_sleep_on(&eeprom.wait_q); + /* bail out if we get interrupted */ + if (signal_pending(current)) + return -EINTR; + } + eeprom.busy++; + for(i = 0; (i < EEPROM_RETRIES) && (restart > 0); i++) + { + restart = 0; + written = 0; + p = file->f_pos; + + + 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 */ + eeprom.busy--; + wake_up_interruptible(&eeprom.wait_q); + 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 */ + + eeprom.busy--; + wake_up_interruptible(&eeprom.wait_q); + if (written == 0 && file->f_pos >= eeprom.size){ + return -ENOSPC; + } + file->f_pos += written; + 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); diff --git a/arch/cris/arch-v10/drivers/gpio.c b/arch/cris/arch-v10/drivers/gpio.c new file mode 100644 index 00000000000..c095de82a0d --- /dev/null +++ b/arch/cris/arch-v10/drivers/gpio.c @@ -0,0 +1,944 @@ +/* $Id: gpio.c,v 1.12 2004/08/24 07:19:59 starvik Exp $ + * + * Etrax general port I/O device + * + * Copyright (c) 1999, 2000, 2001, 2002 Axis Communications AB + * + * Authors: Bjorn Wesen (initial version) + * Ola Knutsson (LED handling) + * Johan Adolfsson (read/set directions, write, port G) + * + * $Log: gpio.c,v $ + * Revision 1.12 2004/08/24 07:19:59 starvik + * Whitespace cleanup + * + * Revision 1.11 2004/05/14 07:58:03 starvik + * Merge of changes from 2.4 + * + * Revision 1.9 2003/09/11 07:29:48 starvik + * Merge of Linux 2.6.0-test5 + * + * Revision 1.8 2003/07/04 08:27:37 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.7 2003/01/10 07:44:07 starvik + * init_ioremap is now called by kernel before drivers are initialized + * + * Revision 1.6 2002/12/11 13:13:57 starvik + * Added arch/ to v10 specific includes + * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) + * + * Revision 1.5 2002/11/20 11:56:11 starvik + * Merge of Linux 2.5.48 + * + * Revision 1.4 2002/11/18 10:10:05 starvik + * Linux 2.5 port of latest gpio.c from Linux 2.4 + * + * Revision 1.20 2002/10/16 21:16:24 johana + * Added support for PA high level interrupt. + * That gives 2ms response time with iodtest for high levels and 2-12 ms + * response time on low levels if the check is not made in + * process.c:cpu_idle() as well. + * + * Revision 1.19 2002/10/14 18:27:33 johana + * Implemented alarm handling so select() now works. + * Latency is around 6-9 ms with a etrax_gpio_wake_up_check() in + * cpu_idle(). + * Otherwise I get 15-18 ms (same as doing the poll in userspace - + * but less overhead). + * TODO? Perhaps we should add the check in IMMEDIATE_BH (or whatever it + * is in 2.4) as well? + * TODO? Perhaps call request_irq()/free_irq() only when needed? + * Increased version to 2.5 + * + * Revision 1.18 2002/10/11 15:02:00 johana + * Mask inverted 8 bit value in setget_input(). + * + * Revision 1.17 2002/06/17 15:53:01 johana + * Added IO_READ_INBITS, IO_READ_OUTBITS, IO_SETGET_INPUT and IO_SETGET_OUTPUT + * that take a pointer as argument and thus can handle 32 bit ports (G) + * correctly. + * These should be used instead of IO_READBITS, IO_SETINPUT and IO_SETOUTPUT. + * (especially if Port G bit 31 is used) + * + * Revision 1.16 2002/06/17 09:59:51 johana + * Returning 32 bit values in the ioctl return value doesn't work if bit + * 31 is set (could happen for port G), so mask it of with 0x7FFFFFFF. + * A new set of ioctl's will be added. + * + * Revision 1.15 2002/05/06 13:19:13 johana + * IO_SETINPUT returns mask with bit set = inputs for PA and PB as well. + * + * Revision 1.14 2002/04/12 12:01:53 johana + * Use global r_port_g_data_shadow. + * Moved gpio_init_port_g() closer to gpio_init() and marked it __init. + * + * Revision 1.13 2002/04/10 12:03:55 johana + * Added support for port G /dev/gpiog (minor 3). + * Changed indentation on switch cases. + * Fixed other spaces to tabs. + * + * Revision 1.12 2001/11/12 19:42:15 pkj + * * Corrected return values from gpio_leds_ioctl(). + * * Fixed compiler warnings. + * + * Revision 1.11 2001/10/30 14:39:12 johana + * Added D() around gpio_write printk. + * + * Revision 1.10 2001/10/25 10:24:42 johana + * Added IO_CFG_WRITE_MODE ioctl and write method that can do fast + * bittoggling in the kernel. (This speeds up programming an FPGA with 450kB + * from ~60 seconds to 4 seconds). + * Added save_flags/cli/restore_flags in ioctl. + * + * Revision 1.9 2001/05/04 14:16:07 matsfg + * Corrected spelling error + * + * Revision 1.8 2001/04/27 13:55:26 matsfg + * Moved initioremap. + * Turns off all LEDS on init. + * Added support for shutdown and powerbutton. + * + * Revision 1.7 2001/04/04 13:30:08 matsfg + * Added bitset and bitclear for leds. Calls init_ioremap to set up memmapping + * + * Revision 1.6 2001/03/26 16:03:06 bjornw + * Needs linux/config.h + * + * Revision 1.5 2001/03/26 14:22:03 bjornw + * Namechange of some config options + * + * Revision 1.4 2001/02/27 13:52:48 bjornw + * malloc.h -> slab.h + * + * Revision 1.3 2001/01/24 15:06:48 bjornw + * gpio_wq correct type + * + * Revision 1.2 2001/01/18 16:07:30 bjornw + * 2.4 port + * + * Revision 1.1 2001/01/18 15:55:16 bjornw + * Verbatim copy of etraxgpio.c from elinux 2.0 added + * + * + */ + +#include <linux/config.h> + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/interrupt.h> + +#include <asm/etraxgpio.h> +#include <asm/arch/svinto.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/irq.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 int gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static ssize_t gpio_write(struct file * file, const char * buf, size_t count, + loff_t *off); +static int gpio_open(struct inode *inode, struct file *filp); +static int gpio_release(struct inode *inode, struct file *filp); +static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait); + +/* private data per open() of this driver */ + +struct gpio_private { + struct gpio_private *next; + /* 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 = 0; + +static int gpio_some_alarms = 0; /* Set if someone uses alarm */ +static unsigned long gpio_pa_irq_enabled_mask = 0; + +/* 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 +}; + +/* 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 = (struct gpio_private *)file->private_data; + unsigned long data; + poll_wait(file, &priv->alarm_wq, wait); + if (priv->minor == GPIO_MINOR_A) { + unsigned long flags; + 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); + save_flags(flags); cli(); + gpio_pa_irq_enabled_mask |= tmp; + *R_IRQ_MASK1_SET = tmp; + restore_flags(flags); + + } 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 + return 0; + + if ((data & priv->highalarm) || + (~data & priv->lowalarm)) { + mask = POLLIN|POLLRDNORM; + } + + DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); + return mask; +} + +int etrax_gpio_wake_up_check(void) +{ + struct gpio_private *priv = alarmlist; + unsigned long data = 0; + int ret = 0; + while (priv) { + 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; + } + return ret; +} + +static irqreturn_t +gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + if (gpio_some_alarms) { + etrax_gpio_wake_up_check(); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static irqreturn_t +gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long tmp; + /* 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; + + if (gpio_some_alarms) { + return IRQ_RETVAL(etrax_gpio_wake_up_check()); + } + return IRQ_NONE; +} + + +static ssize_t gpio_write(struct file * file, const char * buf, size_t count, + loff_t *off) +{ + struct gpio_private *priv = (struct gpio_private *)file->private_data; + unsigned char data, clk_mask, data_mask, write_msb; + unsigned long flags; + 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; + } + clk_mask = priv->clk_mask; + data_mask = priv->data_mask; + /* It must have been configured using the IO_CFG_WRITE_MODE */ + /* Perhaps a better error code? */ + if (clk_mask == 0 || data_mask == 0) { + return -EPERM; + } + write_msb = priv->write_msb; + D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb)); + while (count--) { + int i; + data = *buf++; + if (priv->write_msb) { + for (i = 7; i >= 0;i--) { + local_irq_save(flags); local_irq_disable(); + *priv->port = *priv->shadow &= ~clk_mask; + if (data & 1<<i) + *priv->port = *priv->shadow |= data_mask; + else + *priv->port = *priv->shadow &= ~data_mask; + /* For FPGA: min 5.0ns (DCC) before CCLK high */ + *priv->port = *priv->shadow |= clk_mask; + local_irq_restore(flags); + } + } else { + for (i = 0; i <= 7;i++) { + local_irq_save(flags); local_irq_disable(); + *priv->port = *priv->shadow &= ~clk_mask; + if (data & 1<<i) + *priv->port = *priv->shadow |= data_mask; + else + *priv->port = *priv->shadow &= ~data_mask; + /* For FPGA: min 5.0ns (DCC) before CCLK high */ + *priv->port = *priv->shadow |= clk_mask; + local_irq_restore(flags); + } + } + } + return retval; +} + + + +static int +gpio_open(struct inode *inode, struct file *filp) +{ + struct gpio_private *priv; + int p = MINOR(inode->i_rdev); + + if (p > GPIO_MINOR_LAST) + return -EINVAL; + + priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private), + GFP_KERNEL); + + if (!priv) + return -ENOMEM; + + priv->minor = p; + + /* initialize the io/alarm struct and link it into our alarmlist */ + + priv->next = alarmlist; + alarmlist = priv; + 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 = (void *)priv; + + return 0; +} + +static int +gpio_release(struct inode *inode, struct file *filp) +{ + struct gpio_private *p = alarmlist; + struct gpio_private *todel = (struct gpio_private *)filp->private_data; + + /* 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; + return 0; + } + p = p->next; + } + gpio_some_alarms = 0; + + return 0; +} + +/* Main device API. ioctl's to read/set/clear bits, as well as to + * set alarms to wait for using a subsequent select(). + */ + +unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) +{ + /* Set direction 0=unchanged 1=input, + * return mask with 1=input + */ + unsigned long flags; + if (USE_PORTS(priv)) { + local_irq_save(flags); local_irq_disable(); + *priv->dir = *priv->dir_shadow &= + ~((unsigned char)arg & priv->changeable_dir); + local_irq_restore(flags); + return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */ + } else if (priv->minor == GPIO_MINOR_G) { + /* We must fiddle with R_GEN_CONFIG to change dir */ + save_flags(flags); cli(); + 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_INFO "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 */ + + } + restore_flags(flags); + return dir_g_in_bits; + } + return 0; +} /* setget_input */ + +unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) +{ + unsigned long flags; + if (USE_PORTS(priv)) { + local_irq_save(flags); local_irq_disable(); + *priv->dir = *priv->dir_shadow |= + ((unsigned char)arg & priv->changeable_dir); + local_irq_restore(flags); + return *priv->dir_shadow; + } else if (priv->minor == GPIO_MINOR_G) { + /* We must fiddle with R_GEN_CONFIG to change dir */ + save_flags(flags); cli(); + 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 */ + } + restore_flags(flags); + return dir_g_out_bits & 0x7FFFFFFF; + } + return 0; +} /* setget_output */ + +static int +gpio_leds_ioctl(unsigned int cmd, unsigned long arg); + +static int +gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + unsigned long val; + struct gpio_private *priv = (struct gpio_private *)file->private_data; + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { + return -EINVAL; + } + + switch (_IOC_NR(cmd)) { + case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ + // read the port + if (USE_PORTS(priv)) { + return *priv->port; + } else if (priv->minor == GPIO_MINOR_G) { + return (*R_PORT_G_DATA) & 0x7FFFFFFF; + } + break; + case IO_SETBITS: + local_irq_save(flags); local_irq_disable(); + // set changeable bits with a 1 in arg + 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); + } + local_irq_restore(flags); + break; + case IO_CLRBITS: + local_irq_save(flags); local_irq_disable(); + // clear changeable bits with a 1 in arg + 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); + } + local_irq_restore(flags); + break; + case IO_HIGHALARM: + // set alarm when bits with 1 in arg go high + priv->highalarm |= arg; + gpio_some_alarms = 1; + break; + case IO_LOWALARM: + // set alarm when bits with 1 in arg go low + priv->lowalarm |= arg; + gpio_some_alarms = 1; + break; + case IO_CLRALARM: + // clear alarm for bits with 1 in arg + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + { + /* Must update gpio_some_alarms */ + struct gpio_private *p = alarmlist; + int some_alarms; + some_alarms = 0; + while (p) { + if (p->highalarm | p->lowalarm) { + some_alarms = 1; + break; + } + p = p->next; + } + gpio_some_alarms = some_alarms; + } + break; + case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ + /* Read direction 0=input 1=output */ + if (USE_PORTS(priv)) { + return *priv->dir_shadow; + } else if (priv->minor == GPIO_MINOR_G) { + /* Note: Some bits are both in and out, + * Those that are dual is set here as well. + */ + return (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF; + } + case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ + /* Set direction 0=unchanged 1=input, + * return mask with 1=input + */ + return setget_input(priv, arg) & 0x7FFFFFFF; + break; + case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ + /* Set direction 0=unchanged 1=output, + * return mask with 1=output + */ + return setget_output(priv, arg) & 0x7FFFFFFF; + + case IO_SHUTDOWN: + SOFT_SHUTDOWN(); + break; + case IO_GET_PWR_BT: +#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN) + return (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT)); +#else + return 0; +#endif + break; + case IO_CFG_WRITE_MODE: + 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; + return -EPERM; + } + break; + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + if (USE_PORTS(priv)) { + val = *priv->port; + } else if (priv->minor == GPIO_MINOR_G) { + val = *R_PORT_G_DATA; + } + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + break; + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + if (USE_PORTS(priv)) { + val = *priv->shadow; + } else if (priv->minor == GPIO_MINOR_G) { + val = port_g_data_shadow; + } + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_INPUT: + /* bits set in *arg is set to input, + * *arg updated with current input pins. + */ + if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_OUTPUT: + /* bits set in *arg is set to output, + * *arg updated with current output pins. + */ + if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + if (priv->minor == GPIO_MINOR_LEDS) + return gpio_leds_ioctl(cmd, arg); + else + return -EINVAL; + } /* switch */ + + return 0; +} + +static int +gpio_leds_ioctl(unsigned int cmd, unsigned long arg) +{ + unsigned char green; + unsigned char red; + + switch (_IOC_NR(cmd)) { + case IO_LEDACTIVE_SET: + green = ((unsigned char) arg) & 1; + red = (((unsigned char) arg) >> 1) & 1; + LED_ACTIVE_SET_G(green); + LED_ACTIVE_SET_R(red); + break; + + case IO_LED_SETBIT: + LED_BIT_SET(arg); + break; + + case IO_LED_CLRBIT: + LED_BIT_CLR(arg); + break; + + default: + return -EINVAL; + } /* switch */ + + return 0; +} + +struct file_operations gpio_fops = { + .owner = THIS_MODULE, + .poll = gpio_poll, + .ioctl = gpio_ioctl, + .write = gpio_write, + .open = gpio_open, + .release = gpio_release, +}; + + +static void __init gpio_init_port_g(void) +{ +#define GROUPA (0x0000FF3F) +#define GROUPB (1<<6 | 1<<7) +#define GROUPC (1<<30 | 1<<31) +#define GROUPD (0x3FFF0000) +#define GROUPD_LOW (0x00FF0000) + unsigned long used_in_bits = 0; + unsigned long used_out_bits = 0; + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi0, select)){ + used_in_bits |= GROUPA | GROUPB | 0 | 0; + used_out_bits |= GROUPA | GROUPB | 0 | 0; + } + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ata, select)) { + used_in_bits |= GROUPA | GROUPB | GROUPC | (GROUPD & ~(1<<25|1<<26)); + used_out_bits |= GROUPA | GROUPB | GROUPC | GROUPD; + } + + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, par0, select)) { + used_in_bits |= (GROUPA & ~(1<<0)) | 0 | 0 | 0; + used_out_bits |= (GROUPA & ~(1<<0)) | 0 | 0 | 0; + } + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ser2, select)) { + used_in_bits |= 0 | GROUPB | 0 | 0; + used_out_bits |= 0 | GROUPB | 0 | 0; + } + /* mio same as shared RAM ? */ + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, mio, select)) { + used_in_bits |= (GROUPA & ~(1<<0)) | 0 |0 |GROUPD_LOW; + used_out_bits |= (GROUPA & ~(1<<0|1<<1|1<<2)) | 0 |0 |GROUPD_LOW; + } + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi1, select)) { + used_in_bits |= 0 | 0 | GROUPC | GROUPD; + used_out_bits |= 0 | 0 | GROUPC | GROUPD; + } + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi0w, select)) { + used_in_bits |= GROUPA | GROUPB | 0 | (GROUPD_LOW | 1<<24); + used_out_bits |= GROUPA | GROUPB | 0 | (GROUPD_LOW | 1<<24 | 1<<25|1<<26); + } + + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, par1, select)) { + used_in_bits |= 0 | 0 | 0 | (GROUPD & ~(1<<24)); + used_out_bits |= 0 | 0 | 0 | (GROUPD & ~(1<<24)); + } + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ser3, select)) { + used_in_bits |= 0 | 0 | GROUPC | 0; + used_out_bits |= 0 | 0 | GROUPC | 0; + } + /* mio same as shared RAM-W? */ + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, mio_w, select)) { + used_in_bits |= (GROUPA & ~(1<<0)) | 0 | 0 |GROUPD_LOW; + used_out_bits |= (GROUPA & ~(1<<0|1<<1|1<<2)) | 0 | 0 |GROUPD_LOW; + } + /* TODO: USB p2, parw, sync ser3? */ + + /* 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); + + dir_g_in_bits = ~used_in_bits; + dir_g_out_bits = ~used_out_bits; + + changeable_dir_g = 0x01FFFF01; /* all that can change dir */ + changeable_dir_g &= 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); + + + 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 __init int +gpio_init(void) +{ + int res; +#if defined (CONFIG_ETRAX_CSP0_LEDS) + int i; +#endif + + /* do the formalities */ + + res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); + if (res < 0) { + printk(KERN_ERR "gpio: couldn't get a major number.\n"); + return res; + } + + /* Clear all leds */ +#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) + LED_NETWORK_SET(0); + LED_ACTIVE_SET(0); + LED_DISK_READ(0); + LED_DISK_WRITE(0); + +#if defined (CONFIG_ETRAX_CSP0_LEDS) + for (i = 0; i < 32; i++) { + LED_BIT_SET(i); + } +#endif + +#endif + gpio_init_port_g(); + printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002 Axis Communications AB\n"); + /* We call etrax_gpio_wake_up_check() from timer interrupt and + * from cpu_idle() in kernel/process.c + * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms + * in some tests. + */ + if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, + SA_SHIRQ | SA_INTERRUPT,"gpio poll", NULL)) { + printk(KERN_CRIT "err: timer0 irq for gpio\n"); + } + if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt, + SA_SHIRQ | SA_INTERRUPT,"gpio PA", NULL)) { + 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); diff --git a/arch/cris/arch-v10/drivers/i2c.c b/arch/cris/arch-v10/drivers/i2c.c new file mode 100644 index 00000000000..8bbe233ba7b --- /dev/null +++ b/arch/cris/arch-v10/drivers/i2c.c @@ -0,0 +1,730 @@ +/*!*************************************************************************** +*! +*! FILE NAME : i2c.c +*! +*! DESCRIPTION: implements an interface for IIC/I2C, both directly from other +*! kernel modules (i2c_writereg/readreg) and from userspace using +*! ioctl()'s +*! +*! Nov 30 1998 Torbjorn Eliasson Initial version. +*! Bjorn Wesen Elinux kernel version. +*! Jan 14 2000 Johan Adolfsson Fixed PB shadow register stuff - +*! don't use PB_I2C if DS1302 uses same bits, +*! use PB. +*! $Log: i2c.c,v $ +*! Revision 1.9 2004/08/24 06:49:14 starvik +*! Whitespace cleanup +*! +*! Revision 1.8 2004/06/08 08:48:26 starvik +*! Removed unused code +*! +*! Revision 1.7 2004/05/28 09:26:59 starvik +*! Modified I2C initialization to work in 2.6. +*! +*! Revision 1.6 2004/05/14 07:58:03 starvik +*! Merge of changes from 2.4 +*! +*! Revision 1.4 2002/12/11 13:13:57 starvik +*! Added arch/ to v10 specific includes +*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) +*! +*! Revision 1.3 2002/11/20 11:56:11 starvik +*! Merge of Linux 2.5.48 +*! +*! Revision 1.2 2002/11/18 13:16:06 starvik +*! Linux 2.5 port of latest 2.4 drivers +*! +*! Revision 1.9 2002/10/31 15:32:26 starvik +*! Update Port B register and shadow even when running with hardware support +*! to avoid glitches when reading bits +*! Never set direction to out in i2c_inbyte +*! Removed incorrect clock togling at end of i2c_inbyte +*! +*! Revision 1.8 2002/08/13 06:31:53 starvik +*! Made SDA and SCL line configurable +*! Modified i2c_inbyte to work with PCF8563 +*! +*! Revision 1.7 2001/04/04 13:11:36 markusl +*! Updated according to review remarks +*! +*! Revision 1.6 2001/03/19 12:43:00 markusl +*! Made some symbols unstatic (used by the eeprom driver) +*! +*! Revision 1.5 2001/02/27 13:52:48 bjornw +*! malloc.h -> slab.h +*! +*! Revision 1.4 2001/02/15 07:17:40 starvik +*! Corrected usage if port_pb_i2c_shadow +*! +*! Revision 1.3 2001/01/26 17:55:13 bjornw +*! * Made I2C_USES_PB_NOT_PB_I2C a CONFIG option instead of assigning it +*! magically. Config.in needs to set it for the options that need it, like +*! Dallas 1302 support. Actually, it should be default since it screws up +*! the PB bits even if you don't use I2C.. +*! * Include linux/config.h to get the above +*! +*! Revision 1.2 2001/01/18 15:49:30 bjornw +*! 2.4 port of I2C including some cleanups (untested of course) +*! +*! Revision 1.1 2001/01/18 15:35:25 bjornw +*! Verbatim copy of the Etrax i2c driver, 2.0 elinux version +*! +*! +*! --------------------------------------------------------------------------- +*! +*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN +*! +*!***************************************************************************/ +/* $Id: i2c.c,v 1.9 2004/08/24 06:49:14 starvik Exp $ */ + +/****************** INCLUDE FILES SECTION ***********************************/ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/config.h> + +#include <asm/etraxi2c.h> + +#include <asm/system.h> +#include <asm/arch/svinto.h> +#include <asm/io.h> +#include <asm/delay.h> + +#include "i2c.h" + +/****************** I2C DEFINITION SECTION *************************/ + +#define D(x) + +#define I2C_MAJOR 123 /* LOCAL/EXPERIMENTAL */ +static const char i2c_name[] = "i2c"; + +#define CLOCK_LOW_TIME 8 +#define CLOCK_HIGH_TIME 8 +#define START_CONDITION_HOLD_TIME 8 +#define STOP_CONDITION_HOLD_TIME 8 +#define ENABLE_OUTPUT 0x01 +#define ENABLE_INPUT 0x00 +#define I2C_CLOCK_HIGH 1 +#define I2C_CLOCK_LOW 0 +#define I2C_DATA_HIGH 1 +#define I2C_DATA_LOW 0 + +#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) + + +/****************** FUNCTION DEFINITION SECTION *************************/ + + +/* generate i2c start condition */ + +void +i2c_start(void) +{ + /* + * SCL=1 SDA=1 + */ + i2c_dir_out(); + i2c_delay(CLOCK_HIGH_TIME/6); + i2c_data(I2C_DATA_HIGH); + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + /* + * SCL=1 SDA=0 + */ + i2c_data(I2C_DATA_LOW); + i2c_delay(START_CONDITION_HOLD_TIME); + /* + * SCL=0 SDA=0 + */ + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_LOW_TIME); +} + +/* generate i2c stop condition */ + +void +i2c_stop(void) +{ + i2c_dir_out(); + + /* + * SCL=0 SDA=0 + */ + i2c_clk(I2C_CLOCK_LOW); + i2c_data(I2C_DATA_LOW); + i2c_delay(CLOCK_LOW_TIME*2); + /* + * SCL=1 SDA=0 + */ + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME*2); + /* + * SCL=1 SDA=1 + */ + i2c_data(I2C_DATA_HIGH); + i2c_delay(STOP_CONDITION_HOLD_TIME); + + i2c_dir_in(); +} + +/* write a byte to the i2c interface */ + +void +i2c_outbyte(unsigned char x) +{ + int i; + + i2c_dir_out(); + + for (i = 0; i < 8; i++) { + if (x & 0x80) { + i2c_data(I2C_DATA_HIGH); + } else { + i2c_data(I2C_DATA_LOW); + } + + i2c_delay(CLOCK_LOW_TIME/2); + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_LOW_TIME/2); + x <<= 1; + } + i2c_data(I2C_DATA_LOW); + i2c_delay(CLOCK_LOW_TIME/2); + + /* + * enable input + */ + i2c_dir_in(); +} + +/* read a byte from the i2c interface */ + +unsigned char +i2c_inbyte(void) +{ + unsigned char aBitByte = 0; + int i; + + /* Switch off I2C to get bit */ + i2c_disable(); + i2c_dir_in(); + i2c_delay(CLOCK_HIGH_TIME/2); + + /* Get bit */ + aBitByte |= i2c_getbit(); + + /* Enable I2C */ + i2c_enable(); + i2c_delay(CLOCK_LOW_TIME/2); + + for (i = 1; i < 8; i++) { + aBitByte <<= 1; + /* Clock pulse */ + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_LOW_TIME); + + /* Switch off I2C to get bit */ + i2c_disable(); + i2c_dir_in(); + i2c_delay(CLOCK_HIGH_TIME/2); + + /* Get bit */ + aBitByte |= i2c_getbit(); + + /* Enable I2C */ + i2c_enable(); + i2c_delay(CLOCK_LOW_TIME/2); + } + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + + /* + * we leave the clock low, getbyte is usually followed + * by sendack/nack, they assume the clock to be low + */ + i2c_clk(I2C_CLOCK_LOW); + return aBitByte; +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_getack +*# +*# DESCRIPTION : checks if ack was received from ic2 +*# +*#--------------------------------------------------------------------------*/ + +int +i2c_getack(void) +{ + int ack = 1; + /* + * enable output + */ + i2c_dir_out(); + /* + * Release data bus by setting + * data high + */ + i2c_data(I2C_DATA_HIGH); + /* + * enable input + */ + i2c_dir_in(); + i2c_delay(CLOCK_HIGH_TIME/4); + /* + * generate ACK clock pulse + */ + i2c_clk(I2C_CLOCK_HIGH); + /* + * Use PORT PB instead of I2C + * for input. (I2C not working) + */ + i2c_clk(1); + i2c_data(1); + /* + * switch off I2C + */ + i2c_data(1); + i2c_disable(); + i2c_dir_in(); + /* + * now wait for ack + */ + i2c_delay(CLOCK_HIGH_TIME/2); + /* + * check for ack + */ + if(i2c_getbit()) + ack = 0; + i2c_delay(CLOCK_HIGH_TIME/2); + if(!ack){ + if(!i2c_getbit()) /* receiver pulld SDA low */ + ack = 1; + i2c_delay(CLOCK_HIGH_TIME/2); + } + + /* + * our clock is high now, make sure data is low + * before we enable our output. If we keep data high + * and enable output, we would generate a stop condition. + */ + i2c_data(I2C_DATA_LOW); + + /* + * end clock pulse + */ + i2c_enable(); + i2c_dir_out(); + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_HIGH_TIME/4); + /* + * enable output + */ + i2c_dir_out(); + /* + * remove ACK clock pulse + */ + i2c_data(I2C_DATA_HIGH); + i2c_delay(CLOCK_LOW_TIME/2); + return ack; +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: I2C::sendAck +*# +*# DESCRIPTION : Send ACK on received data +*# +*#--------------------------------------------------------------------------*/ +void +i2c_sendack(void) +{ + /* + * enable output + */ + i2c_delay(CLOCK_LOW_TIME); + i2c_dir_out(); + /* + * set ack pulse high + */ + i2c_data(I2C_DATA_LOW); + /* + * generate clock pulse + */ + i2c_delay(CLOCK_HIGH_TIME/6); + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_LOW_TIME/6); + /* + * reset data out + */ + i2c_data(I2C_DATA_HIGH); + i2c_delay(CLOCK_LOW_TIME); + + i2c_dir_in(); +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_sendnack +*# +*# DESCRIPTION : Sends NACK on received data +*# +*#--------------------------------------------------------------------------*/ +void +i2c_sendnack(void) +{ + /* + * enable output + */ + i2c_delay(CLOCK_LOW_TIME); + i2c_dir_out(); + /* + * set data high + */ + i2c_data(I2C_DATA_HIGH); + /* + * generate clock pulse + */ + i2c_delay(CLOCK_HIGH_TIME/6); + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_LOW_TIME); + + i2c_dir_in(); +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_writereg +*# +*# DESCRIPTION : Writes a value to an I2C device +*# +*#--------------------------------------------------------------------------*/ +int +i2c_writereg(unsigned char theSlave, unsigned char theReg, + unsigned char theValue) +{ + int error, cntr = 3; + unsigned long flags; + + do { + error = 0; + /* + * we don't like to be interrupted + */ + local_irq_save(flags); + local_irq_disable(); + + i2c_start(); + /* + * send slave address + */ + i2c_outbyte((theSlave & 0xfe)); + /* + * wait for ack + */ + if(!i2c_getack()) + error = 1; + /* + * now select register + */ + i2c_dir_out(); + i2c_outbyte(theReg); + /* + * now it's time to wait for ack + */ + if(!i2c_getack()) + error |= 2; + /* + * send register register data + */ + i2c_outbyte(theValue); + /* + * now it's time to wait for ack + */ + if(!i2c_getack()) + error |= 4; + /* + * end byte stream + */ + i2c_stop(); + /* + * enable interrupt again + */ + local_irq_restore(flags); + + } while(error && cntr--); + + i2c_delay(CLOCK_LOW_TIME); + + return -error; +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_readreg +*# +*# DESCRIPTION : Reads a value from the decoder registers. +*# +*#--------------------------------------------------------------------------*/ +unsigned char +i2c_readreg(unsigned char theSlave, unsigned char theReg) +{ + unsigned char b = 0; + int error, cntr = 3; + unsigned long flags; + + do { + error = 0; + /* + * we don't like to be interrupted + */ + local_irq_save(flags); + local_irq_disable(); + /* + * 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_sendack(); + /* + * end sequence + */ + i2c_stop(); + /* + * enable interrupt again + */ + local_irq_restore(flags); + + } while(error && cntr--); + + return b; +} + +static int +i2c_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int +i2c_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +/* Main device API. ioctl's to write or read to/from i2c registers. + */ + +static int +i2c_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) { + return -EINVAL; + } + + switch (_IOC_NR(cmd)) { + case I2C_WRITEREG: + /* write to an i2c slave */ + D(printk("i2cw %d %d %d\n", + I2C_ARGSLAVE(arg), + I2C_ARGREG(arg), + I2C_ARGVALUE(arg))); + + return i2c_writereg(I2C_ARGSLAVE(arg), + I2C_ARGREG(arg), + I2C_ARGVALUE(arg)); + case I2C_READREG: + { + unsigned char val; + /* read from an i2c slave */ + D(printk("i2cr %d %d ", + I2C_ARGSLAVE(arg), + I2C_ARGREG(arg))); + val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg)); + D(printk("= %d\n", val)); + return val; + } + default: + return -EINVAL; + + } + + return 0; +} + +static struct file_operations i2c_fops = { + .owner = THIS_MODULE, + .ioctl = i2c_ioctl, + .open = i2c_open, + .release = i2c_release, +}; + +int __init +i2c_init(void) +{ + /* Setup and enable the Port B I2C interface */ + +#ifndef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C + *R_PORT_PB_I2C = port_pb_i2c_shadow |= + IO_STATE(R_PORT_PB_I2C, i2c_en, on) | + IO_FIELD(R_PORT_PB_I2C, i2c_d, 1) | + IO_FIELD(R_PORT_PB_I2C, i2c_clk, 1) | + IO_STATE(R_PORT_PB_I2C, i2c_oe_, enable); +#endif + + port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir0); + port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir1); + + *R_PORT_PB_DIR = (port_pb_dir_shadow |= + IO_STATE(R_PORT_PB_DIR, dir0, input) | + IO_STATE(R_PORT_PB_DIR, dir1, output)); + + return 0; +} + +static int __init +i2c_register(void) +{ + int res; + + i2c_init(); + res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops); + if(res < 0) { + printk(KERN_ERR "i2c: couldn't get a major number.\n"); + return res; + } + + printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n"); + + return 0; +} + +/* this makes sure that i2c_register is called during boot */ + +module_init(i2c_register); + +/****************** END OF FILE i2c.c ********************************/ diff --git a/arch/cris/arch-v10/drivers/i2c.h b/arch/cris/arch-v10/drivers/i2c.h new file mode 100644 index 00000000000..4ee91426bd4 --- /dev/null +++ b/arch/cris/arch-v10/drivers/i2c.h @@ -0,0 +1,18 @@ +/* $Id: i2c.h,v 1.3 2004/05/28 09:26:59 starvik Exp $ */ + +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); + + + diff --git a/arch/cris/arch-v10/drivers/pcf8563.c b/arch/cris/arch-v10/drivers/pcf8563.c new file mode 100644 index 00000000000..b3dfdf7b8fc --- /dev/null +++ b/arch/cris/arch-v10/drivers/pcf8563.c @@ -0,0 +1,313 @@ +/* + * PCF8563 RTC + * + * From Phillips' datasheet: + * + * The PCF8563 is a CMOS real-time clock/calendar optimized for low power + * consumption. A programmable clock output, interupt output and voltage + * low detector are also provided. All address and data are transferred + * serially via two-line bidirectional I2C-bus. Maximum bus speed is + * 400 kbits/s. The built-in word address register is incremented + * automatically after each written or read bute. + * + * Copyright (c) 2002, Axis Communications AB + * All rights reserved. + * + * Author: Tobias Anderberg <tobiasa@axis.com>. + * + * $Id: pcf8563.c,v 1.8 2004/08/24 06:42:51 starvik Exp $ + */ + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <linux/delay.h> +#include <linux/bcd.h> + +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/arch/svinto.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.8 $" + +/* 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 const unsigned char days_in_month[] = + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); + +static struct file_operations pcf8563_fops = { + .owner = THIS_MODULE, + .ioctl = pcf8563_ioctl, +}; + +unsigned char +pcf8563_readreg(int reg) +{ + unsigned char res = i2c_readreg(RTC_I2C_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_MONTH: + res = (res & 0x1f) - 1; /* PCF8563 returns month in range 1-12 */ + break; + } + return res; +} + +void +pcf8563_writereg(int reg, unsigned char val) +{ +#ifdef CONFIG_ETRAX_RTC_READONLY + if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR)) + return; +#endif + + rtc_write(reg, val); +} + +void +get_rtc_time(struct rtc_time *tm) +{ + tm->tm_sec = rtc_read(RTC_SECONDS); + tm->tm_min = rtc_read(RTC_MINUTES); + tm->tm_hour = rtc_read(RTC_HOURS); + tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH); + tm->tm_mon = rtc_read(RTC_MONTH); + tm->tm_year = rtc_read(RTC_YEAR); + + if (tm->tm_sec & 0x80) + printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); + + tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); + tm->tm_sec &= 0x7f; + tm->tm_min &= 0x7f; + tm->tm_hour &= 0x3f; + tm->tm_mday &= 0x3f; + tm->tm_mon &= 0x1f; + + BCD_TO_BIN(tm->tm_sec); + BCD_TO_BIN(tm->tm_min); + BCD_TO_BIN(tm->tm_hour); + BCD_TO_BIN(tm->tm_mday); + BCD_TO_BIN(tm->tm_mon); + tm->tm_mon--; /* Month is 1..12 in RTC but 0..11 in linux */ +} + +int __init +pcf8563_init(void) +{ + unsigned char ret; + + i2c_init(); + + /* + * First of all we need to reset the chip. This is done by + * clearing control1, control2 and clk freq, clear the + * Voltage Low bit, 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; + + /* Clear the VL bit in the seconds register. */ + ret = rtc_read(RTC_SECONDS); + + if (rtc_write(RTC_SECONDS, (ret & 0x7f)) < 0) + goto err; + + /* Reset the alarms. */ + if (rtc_write(RTC_MINUTE_ALARM, 0x00) < 0) + goto err; + + if (rtc_write(RTC_HOUR_ALARM, 0x00) < 0) + goto err; + + if (rtc_write(RTC_DAY_ALARM, 0x00) < 0) + goto err; + + if (rtc_write(RTC_WEEKDAY_ALARM, 0x00) < 0) + goto err; + + /* Check for low voltage, and warn about it.. */ + if (rtc_read(RTC_SECONDS) & 0x80) + printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); + + return 0; + +err: + printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); + return -1; +} + +void __exit +pcf8563_exit(void) +{ + if (unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME) < 0) { + printk(KERN_INFO "%s: Unable to unregister device.\n", PCF8563_NAME); + } +} + +/* + * ioctl calls for this driver. Why return -ENOTTY upon error? Because + * POSIX says so! + */ +int +pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + /* Some sanity checks. */ + if (_IOC_TYPE(cmd) != RTC_MAGIC) + return -ENOTTY; + + if (_IOC_NR(cmd) > RTC_MAX_IOCTL) + return -ENOTTY; + + switch (cmd) { + case RTC_RD_TIME: + { + struct rtc_time tm; + + get_rtc_time(&tm); + + if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) { + return -EFAULT; + } + + return 0; + } + break; + case RTC_SET_TIME: + { +#ifdef CONFIG_ETRAX_RTC_READONLY + return -EPERM; +#else + int leap; + int century; + struct rtc_time tm; + + memset(&tm, 0, sizeof (struct rtc_time)); + if (!capable(CAP_SYS_TIME)) + return -EPERM; + + if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(struct rtc_time))) + return -EFAULT; + + /* Convert from struct tm to struct rtc_time. */ + tm.tm_year += 1900; + tm.tm_mon += 1; + + leap = ((tm.tm_mon == 2) && ((tm.tm_year % 4) == 0)) ? 1 : 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_hour >= 24) || + (tm.tm_min >= 60) || + (tm.tm_sec >= 60)) + return -EINVAL; + + century = (tm.tm_year >= 2000) ? 0x80 : 0; + tm.tm_year = tm.tm_year % 100; + + BIN_TO_BCD(tm.tm_year); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_sec); + tm.tm_mon |= century; + + rtc_write(RTC_YEAR, tm.tm_year); + rtc_write(RTC_MONTH, tm.tm_mon); + rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); + rtc_write(RTC_HOURS, tm.tm_hour); + rtc_write(RTC_MINUTES, tm.tm_min); + rtc_write(RTC_SECONDS, tm.tm_sec); + + return 0; +#endif /* !CONFIG_ETRAX_RTC_READONLY */ + } + + case RTC_VLOW_RD: + { + int vl_bit = 0; + + if (rtc_read(RTC_SECONDS) & 0x80) { + vl_bit = 1; + printk(KERN_WARNING "%s: RTC Voltage Low - reliable " + "date/time information is no longer guaranteed!\n", + PCF8563_NAME); + } + if (copy_to_user((int *) arg, &vl_bit, sizeof(int))) + return -EFAULT; + + return 0; + } + + case RTC_VLOW_SET: + { + /* Clear the VL bit in the seconds register */ + int ret = rtc_read(RTC_SECONDS); + + rtc_write(RTC_SECONDS, (ret & 0x7F)); + + return 0; + } + + default: + return -ENOTTY; + } + + return 0; +} + +static int __init +pcf8563_register(void) +{ + pcf8563_init(); + if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { + printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n", + PCF8563_NAME, PCF8563_MAJOR); + return -1; + } + + printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); + return 0; +} + +module_init(pcf8563_register); +module_exit(pcf8563_exit); diff --git a/arch/cris/arch-v10/kernel/Makefile b/arch/cris/arch-v10/kernel/Makefile new file mode 100644 index 00000000000..52761603b6a --- /dev/null +++ b/arch/cris/arch-v10/kernel/Makefile @@ -0,0 +1,17 @@ +# $Id: Makefile,v 1.5 2004/06/02 08:24:38 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 + +obj-$(CONFIG_ETRAX_KGDB) += kgdb.o +obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o +obj-$(CONFIG_MODULES) += crisksyms.o + +clean: + diff --git a/arch/cris/arch-v10/kernel/asm-offsets.c b/arch/cris/arch-v10/kernel/asm-offsets.c new file mode 100644 index 00000000000..1aa3cc4e710 --- /dev/null +++ b/arch/cris/arch-v10/kernel/asm-offsets.c @@ -0,0 +1,47 @@ +#include <linux/sched.h> +#include <asm/thread_info.h> + +/* + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed to extract + * and format the required data. + */ + +#define DEFINE(sym, val) \ + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + +#define BLANK() asm volatile("\n->" : : ) + +int main(void) +{ +#define ENTRY(entry) DEFINE(PT_ ## entry, offsetof(struct pt_regs, entry)) + ENTRY(orig_r10); + ENTRY(r13); + ENTRY(r12); + ENTRY(r11); + ENTRY(r10); + ENTRY(r9); + ENTRY(mof); + ENTRY(dccr); + ENTRY(srp); + BLANK(); +#undef ENTRY +#define ENTRY(entry) DEFINE(TI_ ## entry, offsetof(struct thread_info, entry)) + ENTRY(task); + ENTRY(flags); + ENTRY(preempt_count); + BLANK(); +#undef ENTRY +#define ENTRY(entry) DEFINE(THREAD_ ## entry, offsetof(struct thread_struct, entry)) + ENTRY(ksp); + ENTRY(usp); + ENTRY(dccr); + BLANK(); +#undef ENTRY +#define ENTRY(entry) DEFINE(TASK_ ## entry, offsetof(struct task_struct, entry)) + ENTRY(pid); + BLANK(); + DEFINE(LCLONE_VM, CLONE_VM); + DEFINE(LCLONE_UNTRACED, CLONE_UNTRACED); + return 0; +} diff --git a/arch/cris/arch-v10/kernel/crisksyms.c b/arch/cris/arch-v10/kernel/crisksyms.c new file mode 100644 index 00000000000..b332bf9b312 --- /dev/null +++ b/arch/cris/arch-v10/kernel/crisksyms.c @@ -0,0 +1,17 @@ +#include <linux/config.h> +#include <linux/module.h> +#include <asm/io.h> +#include <asm/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); diff --git a/arch/cris/arch-v10/kernel/debugport.c b/arch/cris/arch-v10/kernel/debugport.c new file mode 100644 index 00000000000..6cf069e5e7b --- /dev/null +++ b/arch/cris/arch-v10/kernel/debugport.c @@ -0,0 +1,531 @@ +/* Serialport functions for debugging + * + * Copyright (c) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen + * + * Exports: + * console_print_etrax(char *buf) + * int getDebugChar() + * putDebugChar(int) + * enableDebugIRQ() + * init_etrax_debug() + * + * $Log: debugport.c,v $ + * Revision 1.19 2004/10/21 07:26:16 starvik + * Made it possible to specify console settings on kernel command line. + * + * Revision 1.18 2004/10/19 13:07:37 starvik + * Merge of Linux 2.6.9 + * + * Revision 1.17 2004/09/29 10:33:46 starvik + * Resolved a dealock when printing debug from kernel. + * + * Revision 1.16 2004/08/24 06:12:19 starvik + * Whitespace cleanup + * + * Revision 1.15 2004/08/16 12:37:19 starvik + * Merge of Linux 2.6.8 + * + * Revision 1.14 2004/05/17 13:11:29 starvik + * Disable DMA until real serial driver is up + * + * Revision 1.13 2004/05/14 07:58:01 starvik + * Merge of changes from 2.4 + * + * Revision 1.12 2003/09/11 07:29:49 starvik + * Merge of Linux 2.6.0-test5 + * + * Revision 1.11 2003/07/07 09:53:36 starvik + * Revert all the 2.5.74 merge changes to make the console work again + * + * Revision 1.9 2003/02/17 17:07:23 starvik + * Solved the problem with corrupted debug output (from Linux 2.4) + * * Wait until DMA, FIFO and pipe is empty before and after transmissions + * * Buffer data until a FIFO flush can be triggered. + * + * Revision 1.8 2003/01/22 06:48:36 starvik + * Fixed warnings issued by GCC 3.2.1 + * + * Revision 1.7 2002/12/12 08:26:32 starvik + * Don't use C-comments inside CVS comments + * + * Revision 1.6 2002/12/11 15:42:02 starvik + * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/ + * + * Revision 1.5 2002/11/20 06:58:03 starvik + * Compiles with kgdb + * + * Revision 1.4 2002/11/19 14:35:24 starvik + * Changes from linux 2.4 + * Changed struct initializer syntax to the currently prefered notation + * + * Revision 1.3 2002/11/06 09:47:03 starvik + * Modified for new interrupt macros + * + * Revision 1.2 2002/01/21 15:21:50 bjornw + * Update for kdev_t changes + * + * Revision 1.6 2001/04/17 13:58:39 orjanf + * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. + * + * Revision 1.5 2001/03/26 14:22:05 bjornw + * Namechange of some config options + * + * Revision 1.4 2000/10/06 12:37:26 bjornw + * Use physical addresses when talking to DMA + * + * + */ + +#include <linux/config.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/major.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <asm/system.h> +#include <asm/arch/svinto.h> +#include <asm/io.h> /* Get SIMCOUT. */ + +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) + }, + { + 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) + }, + { + 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) + }, + { + 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) + } +}; + +static struct tty_driver *serial_driver; + +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 +/* Used by serial.c to register a debug_write_function so that the normal + * serial driver is used for kernel debug output + */ +typedef int (*debugport_write_function)(int i, const char *buf, unsigned int len); + +debugport_write_function debug_write_function = NULL; + +static void +start_port(void) +{ + unsigned long rec_ctrl = 0; + unsigned long tr_ctrl = 0; + + if (!port) + return; + + if (port->started) + return; + port->started = 1; + + if (port->index == 0) + { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused); + } + else if (port->index == 1) + { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb); + } + else if (port->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; + + *port->xoff = + IO_STATE(R_SERIAL0_XOFF, tx_stop, enable) | + IO_STATE(R_SERIAL0_XOFF, auto_xoff, disable) | + IO_FIELD(R_SERIAL0_XOFF, xoff_char, 0); + + switch (port->baudrate) + { + case 0: + case 115200: + *port->baud = + IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) | + IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz); + break; + case 1200: + *port->baud = + IO_STATE(R_SERIAL0_BAUD, tr_baud, c1200Hz) | + IO_STATE(R_SERIAL0_BAUD, rec_baud, c1200Hz); + break; + case 2400: + *port->baud = + IO_STATE(R_SERIAL0_BAUD, tr_baud, c2400Hz) | + IO_STATE(R_SERIAL0_BAUD, rec_baud, c2400Hz); + break; + case 4800: + *port->baud = + IO_STATE(R_SERIAL0_BAUD, tr_baud, c4800Hz) | + IO_STATE(R_SERIAL0_BAUD, rec_baud, c4800Hz); + break; + case 9600: + *port->baud = + IO_STATE(R_SERIAL0_BAUD, tr_baud, c9600Hz) | + IO_STATE(R_SERIAL0_BAUD, rec_baud, c9600Hz); + break; + case 19200: + *port->baud = + IO_STATE(R_SERIAL0_BAUD, tr_baud, c19k2Hz) | + IO_STATE(R_SERIAL0_BAUD, rec_baud, c19k2Hz); + break; + case 38400: + *port->baud = + IO_STATE(R_SERIAL0_BAUD, tr_baud, c38k4Hz) | + IO_STATE(R_SERIAL0_BAUD, rec_baud, c38k4Hz); + break; + case 57600: + *port->baud = + IO_STATE(R_SERIAL0_BAUD, tr_baud, c57k6Hz) | + IO_STATE(R_SERIAL0_BAUD, rec_baud, c57k6Hz); + break; + default: + *port->baud = + IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) | + IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz); + break; + } + + if (port->parity == 'E') { + rec_ctrl = + IO_STATE(R_SERIAL0_REC_CTRL, rec_par, even) | + IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable); + tr_ctrl = + IO_STATE(R_SERIAL0_TR_CTRL, tr_par, even) | + IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, enable); + } else if (port->parity == 'O') { + 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 (port->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); + } + + *port->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; + + *port->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; + local_irq_save(flags); + /* Send data */ + for (i = 0; i < len; i++) { + /* Wait until transmitter is ready and send.*/ + while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready))) + ; + *port->write = buf[i]; + } + 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 + + start_port(); + +#ifdef CONFIG_ETRAX_KGDB + /* kgdb needs to output debug info using the gdb protocol */ + putDebugString(buf, len); + return; +#endif + + if (debug_write_function) + debug_write_function(co->index, buf, len); + else + console_write_direct(co, buf, len); +} + +/* 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; + + do { + readval = *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) +{ + while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready))) + ; + *port->write = val; +} + +/* Enable irq for receiving chars on the debug port, used by kgdb */ + +void +enableDebugIRQ(void) +{ + *R_IRQ_MASK1_SET = port->irq; + /* use R_VECT_MASK directly, since we really bypass Linux normal + * IRQ handling in kgdb anyway, we don't need to use enable_irq + */ + *R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set); + + *port->rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable); +} + +static struct tty_driver* +etrax_console_device(struct console* co, int *index) +{ + return serial_driver; +} + +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(); + } + return 0; +} + +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) { + if (!port) { + register_console(&sercons0); + register_console(&sercons1); + register_console(&sercons2); + register_console(&sercons3); + unregister_console(&sercons); + } + return 0; + } + first = 0; + if (port) + register_console(&sercons); + return 0; +} + +int __init +init_console(void) +{ + serial_driver = alloc_tty_driver(1); + if (!serial_driver) + return -ENOMEM; + return 0; +} + +__initcall(init_etrax_debug); diff --git a/arch/cris/arch-v10/kernel/entry.S b/arch/cris/arch-v10/kernel/entry.S new file mode 100644 index 00000000000..1bc44f481c3 --- /dev/null +++ b/arch/cris/arch-v10/kernel/entry.S @@ -0,0 +1,1132 @@ +/* $Id: entry.S,v 1.23 2004/10/19 13:07:37 starvik Exp $ + * + * linux/arch/cris/entry.S + * + * Copyright (C) 2000, 2001, 2002 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + * $Log: entry.S,v $ + * Revision 1.23 2004/10/19 13:07:37 starvik + * Merge of Linux 2.6.9 + * + * Revision 1.22 2004/06/21 10:29:55 starvik + * Merge of Linux 2.6.7 + * + * Revision 1.21 2004/06/09 05:30:27 starvik + * Clean up multiple interrupt handling. + * Prevent interrupts from interrupting each other. + * Handle all active interrupts. + * + * Revision 1.20 2004/06/08 08:55:32 starvik + * Removed unused code + * + * Revision 1.19 2004/06/04 11:56:15 starvik + * Implemented page table lookup for refills in assembler for improved performance. + * + * Revision 1.18 2004/05/11 12:28:25 starvik + * Merge of Linux 2.6.6 + * + * Revision 1.17 2003/09/11 07:29:49 starvik + * Merge of Linux 2.6.0-test5 + * + * Revision 1.16 2003/07/04 08:27:41 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.15 2003/04/09 07:32:55 starvik + * resume should return task_struct, not thread_info + * + * Revision 1.14 2003/04/09 05:20:44 starvik + * Merge of Linux 2.5.67 + * + * Revision 1.13 2002/12/11 15:42:02 starvik + * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/*.c + * + * Revision 1.12 2002/12/10 09:00:10 starvik + * Merge of Linux 2.5.51 + * + * Revision 1.11 2002/12/05 07:53:10 starvik + * Corrected constants used with btstq + * + * Revision 1.10 2002/11/27 08:45:10 starvik + * pid is in task_struct, not thread_info + * + * Revision 1.9 2002/11/26 09:52:05 starvik + * Added preemptive kernel scheduling (if CONFIG_PREEMPT) + * + * Revision 1.8 2002/11/20 11:56:11 starvik + * Merge of Linux 2.5.48 + * + * Revision 1.7 2002/11/18 13:02:42 starvik + * Added fourth parameter to do_notify_resume + * Minor cleanup + * + * Revision 1.6 2002/11/11 10:37:50 starvik + * Use new asm-offset defines + * Modified for new location of current->work etc + * Removed SYMBOL_NAME from syscalls + * Added some new syscalls + * + * Revision 1.5 2002/11/05 06:45:11 starvik + * Merge of Linux 2.5.45 + * + * Revision 1.4 2002/02/05 15:41:31 bjornw + * Rewritten to conform better to current 2.5 code (similar to arch/i386) + * + * Revision 1.3 2002/01/21 15:22:20 bjornw + * NICE_DOGGY fix from 2.4 arch/cris + * + * Revision 1.37 2001/12/07 17:03:55 bjornw + * Call a c-hook called watchdog_bite_hook instead of show_registers directly + * + * Revision 1.36 2001/11/22 13:36:36 bjornw + * * In ret_from_intr, check regs->dccr for usermode reentrance instead of + * DCCR explicitely (because the latter might not reflect current reality) + * * In mmu_bus_fault, set $r9 _after_ calling the C-code instead of before + * since $r9 is call-clobbered and is potentially needed afterwards + * + * Revision 1.35 2001/10/30 17:10:15 bjornw + * Add some syscalls + * + * Revision 1.34 2001/10/01 14:45:03 bjornw + * Removed underscores and added register prefixes + * + * Revision 1.33 2001/08/21 13:48:01 jonashg + * Added fix by HP to avoid oops when doing a hard_reset_now. + * + * Revision 1.32 2001/08/14 04:32:02 hp + * In _resume, add comment why R9 is saved; don't sound like it's call-saved. + * + * Revision 1.31 2001/07/25 16:07:42 bjornw + * softirq_active/mask -> softirq_pending only + * + * Revision 1.30 2001/07/05 01:03:32 hp + * - include asm/errno.h to get ENOSYS. + * - Use ENOSYS, not local constant LENOSYS; tweak comments. + * - Explain why .include, not #include is used. + * - Make oops-register-dump if watchdog bits and it's not expected. + * - Don't jsr, use jump _hard_reset_now, and skip spurious nop. + * - Use correct section attribute for section .rodata. + * - Adjust sys_ni_syscall fill number. + * + * Revision 1.29 2001/06/25 14:07:00 hp + * Fix review comment. + * * head.S: Use IO_STATE, IO_FIELD and IO_MASK constructs instead of + * magic numbers. Add comment that -traditional must not be used. + * * entry.S (SYMBOL_NAME): Change redefinition to use ## concatenation. + * Correct and update comment. + * * Makefile (.S.o): Don't use -traditional. Add comment why the + * toplevel rule can't be used (now that there's a reason). + * + * Revision 1.28 2001/06/21 02:00:40 hp + * * entry.S: Include asm/unistd.h. + * (_sys_call_table): Use section .rodata, not .data. + * (_kernel_thread): Move from... + * * process.c: ... here. + * * entryoffsets.c (VAL): Break out from... + * (OF): Use VAL. + * (LCLONE_VM): New asmified value from CLONE_VM. + * + * Revision 1.27 2001/05/29 11:25:27 markusl + * In case of "spurious_interrupt", do hard_reset instead of hanging system in a loop... + * + * Revision 1.26 2001/05/15 15:46:03 bjornw + * Include config.h now that we use some CONFIG_ options + * + * Revision 1.25 2001/05/15 05:38:47 hp + * Tweaked code in _ret_from_sys_call + * + * Revision 1.24 2001/05/15 05:27:49 hp + * Save r9 in r1 over function call rather than on stack. + * + * Revision 1.23 2001/05/15 05:10:00 hp + * Generate entry.S structure offsets from C + * + * Revision 1.22 2001/04/17 13:58:39 orjanf + * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. + * + * Revision 1.21 2001/04/17 11:33:29 orjanf + * Updated according to review: + * * Included asm/sv_addr_ag.h to get macro for internal register. + * * Corrected comment regarding system call argument passing. + * * Removed comment about instruction being in a delay slot. + * * Added comment about SYMBOL_NAME macro. + * + * Revision 1.20 2001/04/12 08:51:07 hp + * - Add entry for sys_fcntl64. In fact copy last piece from i386 including ... + * - .rept to fill table to safe state with sys_ni_syscall. + * + * Revision 1.19 2001/04/04 09:43:32 orjanf + * * Moved do_sigtrap from traps.c to entry.S. + * * LTASK_PID need not be global anymore. + * + * Revision 1.18 2001/03/26 09:25:02 markusl + * Updated after review, should now handle USB interrupts correctly. + * + * Revision 1.17 2001/03/21 16:12:55 bjornw + * * Always make room for the cpu status record in the frame, in order to + * use the same framelength and layout for both mmu busfaults and normal + * irqs. No need to check for the explicit CRIS_FRAME_FIXUP type anymore. + * * Fixed bug with using addq for popping the stack in the epilogue - it + * destroyed the flag register. Use instructions that don't affect the + * flag register instead. + * * Removed write to R_PORT_PA_DATA during spurious_interrupt + * + * Revision 1.16 2001/03/20 19:43:02 bjornw + * * Get rid of esp0 setting + * * Give a 7th argument to a systemcall - the stackframe + * + * Revision 1.15 2001/03/05 13:14:30 bjornw + * Spelling fix + * + * Revision 1.14 2001/02/23 08:36:36 perf + * New ABI; syscallnr=r9, arg5=mof, arg6=srp. + * Corrected tracesys call check. + * + * Revision 1.13 2001/02/15 08:40:55 perf + * H-P by way of perf; + * - (_system_call): Don't read system call function address into r1. + * - (RBFExit): There is no such thing as a null pop. Adjust sp by addq. + * - (_system_call): Don't use r10 and don't save and restore it. + * - (THREAD_ESP0): New constant. + * - (_system_call): Inline set_esp0. + * + * Revision 1.12 2001/01/31 17:56:25 orjanf + * Added definition of LTASK_PID and made it global. + * + * Revision 1.11 2001/01/10 21:13:29 bjornw + * SYMBOL_NAME is defined incorrectly for the compiler options we currently use + * + * Revision 1.10 2000/12/18 23:47:56 bjornw + * * Added syscall trace support (ptrace), completely untested of course + * * Removed redundant check for NULL entries in syscall_table + * + * Revision 1.9 2000/11/21 16:40:51 bjornw + * * New frame type used when an SBFS frame needs to be popped without + * actually restarting the instruction + * * Enable interrupts in signal_return (they did so in x86, I hope it's a good + * idea) + * + * Revision 1.8 2000/11/17 16:53:35 bjornw + * Added detection of frame-type in Rexit, so that mmu_bus_fault can + * use ret_from_intr in the return-path to check for signals (like SEGV) + * and other foul things that might have occurred during the fault. + * + * Revision 1.7 2000/10/06 15:04:28 bjornw + * Include mof in register savings + * + * Revision 1.6 2000/09/12 16:02:44 bjornw + * Linux-2.4.0-test7 derived updates + * + * Revision 1.5 2000/08/17 15:35:15 bjornw + * 2.4.0-test6 changed local_irq_count and friends API + * + * Revision 1.4 2000/08/02 13:59:30 bjornw + * Removed olduname and uname from the syscall list + * + * Revision 1.3 2000/07/31 13:32:58 bjornw + * * Export ret_from_intr + * * _resume updated (prev/last tjohejsan) + * * timer_interrupt obsolete + * * SIGSEGV detection in mmu_bus_fault temporarily disabled + * + * + */ + +/* + * 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/config.h> +#include <linux/linkage.h> +#include <linux/sys.h> +#include <asm/unistd.h> +#include <asm/arch/sv_addr_ag.h> +#include <asm/errno.h> +#include <asm/thread_info.h> +#include <asm/arch/offset.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: + ; 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 + move.d PREEMPT_ACTIVE, $r10 + move.d $r10, [$r0+TI_preempt_count] ; Mark as active + ei + jsr schedule + clear.d [$r0+TI_preempt_count] ; Mark as inactive + di + ; 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 + moveq 0, $r11 ; oldset param - 0 in this case + move.d $sp, $r12 ; the regs param + move.d $r1, $r13 ; 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 PMD_SHIFT, $r1 ; Get PMD index into PGD (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 PMD (bit 13-24) + 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: + +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) +;; If we receive a watchdog interrupt while it is not expected, then set +;; up a canonical frame and dump register contents before dying. + + ;; this prologue MUST match the one in irq.h and the struct in ptregs.h!!! + move $brp,[$sp=$sp-16]; instruction pointer and room for a fake SBFS frame + push $srp + 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 + +;; We don't check that we actually were bit by the watchdog as opposed to +;; an external NMI, since there is currently no handler for external NMI. + +;; 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 + + moveq 2, $r2 ; first bit we care about is the timer0 irq + move.d [R_VECT_MASK_RD], $r0; read the irq bits that triggered the multiple irq + move.d $r0, [R_VECT_MASK_CLR] ; Block all active IRQs +1: + btst $r2, $r0 ; check for the irq given by bit r2 + bpl 2f + move.d $r2, $r10 ; First argument to do_IRQ + move.d $sp, $r11 ; second argument to do_IRQ + jsr do_IRQ +2: + addq 1, $r2 ; next vector bit + cmp.b 32, $r2 + bne 1b ; process all irq's up to and including number 31 + moveq 0, $r9 ; make ret_from_intr realise we came from an ir + + move.d $r0, [R_VECT_MASK_SET] ; Unblock all the IRQs + jump ret_from_intr + +do_sigtrap: + ;; + ;; SIGTRAP the process that executed the break instruction. + ;; Make a frame that Rexit in entry.S expects. + ;; + move $brp, [$sp=$sp-16] ; Push BRP while faking a cpu status record. + push $srp ; Push subroutine return pointer. + push $dccr ; Push condition codes. + push $mof ; Push multiply overflow reg. + di ; Need to disable irq's at this point. + subq 14*4, $sp ; Make room for r0-r13. + movem $r13, [$sp] ; Push the r0-r13 registers. + push $r10 ; Push orig_r10. + clear.d [$sp=$sp-4] ; Frametype - this is a normal stackframe. + + movs.w -8192,$r9 ; THREAD_SIZE == 8192 + and.d $sp, $r9 + move.d [$r9+TI_task], $r10 + move.d [$r10+TASK_pid], $r10 ; current->pid as arg1. + moveq 5, $r11 ; SIGTRAP as arg2. + jsr sys_kill + jump ret_from_intr ; Use the return routine for interrupts. + +gdb_handle_breakpoint: + push $dccr + push $r0 +#ifdef CONFIG_ETRAX_KGDB + move $dccr, $r0 ; U-flag not affected by previous insns. + btstq 8, $r0 ; Test the U-flag. + bmi _ugdb_handle_breakpoint ; Go to user mode debugging. + nop ; Empty delay slot (cannot pop r0 here). + pop $r0 ; Restore r0. + ba kgdb_handle_breakpoint ; Go to kernel debugging. + pop $dccr ; Restore dccr in delay slot. +#endif + +_ugdb_handle_breakpoint: + move $brp, $r0 ; Use r0 temporarily for calculation. + subq 2, $r0 ; Set to address of previous instruction. + move $r0, $brp + pop $r0 ; Restore r0. + ba do_sigtrap ; SIGTRAP the offending process. + pop $dccr ; Restore dccr in delay slot. + + .data + +hw_bp_trigs: + .space 64*4 +hw_bp_trig_ptr: + .dword hw_bp_trigs + + .section .rodata,"a" +sys_call_table: + .long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */ + .long sys_exit + .long sys_fork + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_waitpid + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_lchown16 + .long sys_ni_syscall /* old break syscall holder */ + .long sys_stat + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid16 + .long sys_getuid16 + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_fstat + .long sys_pause + .long sys_utime /* 30 */ + .long sys_ni_syscall /* old stty syscall holder */ + .long sys_ni_syscall /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid16 + .long sys_getgid16 + .long sys_signal + .long sys_geteuid16 + .long sys_getegid16 /* 50 */ + .long sys_acct + .long sys_umount /* recycled never used phys( */ + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_ni_syscall /* old sys_olduname holder */ + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_sgetmask + .long sys_ssetmask + .long sys_setreuid16 /* 70 */ + .long sys_setregid16 + .long sys_sigsuspend + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_old_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups16 /* 80 */ + .long sys_setgroups16 + .long sys_select /* was old_select in Linux/E100 */ + .long sys_symlink + .long sys_lstat + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long old_readdir + .long old_mmap /* 90 */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown16 /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ni_syscall /* sys_ioperm in i386 */ + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_ni_syscall /* old sys_uname holder */ + .long sys_ni_syscall /* sys_iopl in i386 */ + .long sys_vhangup + .long sys_ni_syscall /* old "idle" system call */ + .long sys_ni_syscall /* vm86old in i386 */ + .long sys_wait4 + .long sys_swapoff /* 115 */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn + .long sys_clone /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_ni_syscall /* sys_modify_ldt */ + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_ni_syscall /* old "create_module" */ + .long sys_init_module + .long sys_delete_module + .long sys_ni_syscall /* 130: old "get_kernel_syms" */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* for afs_syscall */ + .long sys_setfsuid16 + .long sys_setfsgid16 + .long sys_llseek /* 140 */ + .long sys_getdents + .long sys_select + .long sys_flock + .long sys_msync + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_mremap + .long sys_setresuid16 + .long sys_getresuid16 /* 165 */ + .long sys_ni_syscall /* sys_vm86 */ + .long sys_ni_syscall /* Old sys_query_module */ + .long sys_poll + .long sys_nfsservctl + .long sys_setresgid16 /* 170 */ + .long sys_getresgid16 + .long sys_prctl + .long sys_rt_sigreturn + .long sys_rt_sigaction + .long sys_rt_sigprocmask /* 175 */ + .long sys_rt_sigpending + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long sys_rt_sigsuspend + .long sys_pread64 /* 180 */ + .long sys_pwrite64 + .long sys_chown16 + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* streams1 */ + .long sys_ni_syscall /* streams2 */ + .long sys_vfork /* 190 */ + .long sys_getrlimit + .long sys_mmap2 + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_lchown + .long sys_getuid + .long sys_getgid /* 200 */ + .long sys_geteuid + .long sys_getegid + .long sys_setreuid + .long sys_setregid + .long sys_getgroups /* 205 */ + .long sys_setgroups + .long sys_fchown + .long sys_setresuid + .long sys_getresuid + .long sys_setresgid /* 210 */ + .long sys_getresgid + .long sys_chown + .long sys_setuid + .long sys_setgid + .long sys_setfsuid /* 215 */ + .long sys_setfsgid + .long sys_pivot_root + .long sys_mincore + .long sys_madvise + .long sys_getdents64 /* 220 */ + .long sys_fcntl64 + .long sys_ni_syscall /* reserved for TUX */ + .long sys_ni_syscall + .long sys_gettid + .long sys_readahead /* 225 */ + .long sys_setxattr + .long sys_lsetxattr + .long sys_fsetxattr + .long sys_getxattr + .long sys_lgetxattr /* 230 */ + .long sys_fgetxattr + .long sys_listxattr + .long sys_llistxattr + .long sys_flistxattr + .long sys_removexattr /* 235 */ + .long sys_lremovexattr + .long sys_fremovexattr + .long sys_tkill + .long sys_sendfile64 + .long sys_futex /* 240 */ + .long sys_sched_setaffinity + .long sys_sched_getaffinity + .long sys_ni_syscall /* sys_set_thread_area */ + .long sys_ni_syscall /* sys_get_thread_area */ + .long sys_io_setup /* 245 */ + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit + .long sys_io_cancel + .long sys_fadvise64 /* 250 */ + .long sys_ni_syscall + .long sys_exit_group + .long sys_lookup_dcookie + .long sys_epoll_create + .long sys_epoll_ctl /* 255 */ + .long sys_epoll_wait + .long sys_remap_file_pages + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 260 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 265 */ + .long sys_clock_getres + .long sys_clock_nanosleep + .long sys_statfs64 + .long sys_fstatfs64 + .long sys_tgkill /* 270 */ + .long sys_utimes + .long sys_fadvise64_64 + .long sys_ni_syscall /* sys_vserver */ + .long sys_ni_syscall /* sys_mbind */ + .long sys_ni_syscall /* 275 sys_get_mempolicy */ + .long sys_ni_syscall /* sys_set_mempolicy */ + .long sys_mq_open + .long sys_mq_unlink + .long sys_mq_timedsend + .long sys_mq_timedreceive /* 280 */ + .long sys_mq_notify + .long sys_mq_getsetattr + .long sys_ni_syscall /* reserved for kexec */ + .long sys_waitid + + /* + * NOTE!! This doesn't have to be exact - we just have + * to make sure we have _enough_ of the "sys_ni_syscall" + * entries. Don't panic if you notice that this hasn't + * been shrunk every time we add a new system call. + */ + + .rept NR_syscalls-(.-sys_call_table)/4 + .long sys_ni_syscall + .endr + diff --git a/arch/cris/arch-v10/kernel/fasttimer.c b/arch/cris/arch-v10/kernel/fasttimer.c new file mode 100644 index 00000000000..4717f7ae8e5 --- /dev/null +++ b/arch/cris/arch-v10/kernel/fasttimer.c @@ -0,0 +1,977 @@ +/* $Id: fasttimer.c,v 1.6 2004/05/14 10:18:39 starvik Exp $ + * linux/arch/cris/kernel/fasttimer.c + * + * Fast timers for ETRAX100/ETRAX100LX + * This may be useful in other OS than Linux so use 2 space indentation... + * + * $Log: fasttimer.c,v $ + * Revision 1.6 2004/05/14 10:18:39 starvik + * Export fast_timer_list + * + * Revision 1.5 2004/05/14 07:58:01 starvik + * Merge of changes from 2.4 + * + * Revision 1.4 2003/07/04 08:27:41 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.3 2002/12/12 08:26:32 starvik + * Don't use C-comments inside CVS comments + * + * Revision 1.2 2002/12/11 15:42:02 starvik + * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/ + * + * Revision 1.1 2002/11/18 07:58:06 starvik + * Fast timers (from Linux 2.4) + * + * Revision 1.5 2002/10/15 06:21:39 starvik + * Added call to init_waitqueue_head + * + * Revision 1.4 2002/05/28 17:47:59 johana + * Added del_fast_timer() + * + * Revision 1.3 2002/05/28 16:16:07 johana + * Handle empty fast_timer_list + * + * Revision 1.2 2002/05/27 15:38:42 johana + * Made it compile without warnings on Linux 2.4. + * (includes, wait_queue, PROC_FS and snprintf) + * + * Revision 1.1 2002/05/27 15:32:25 johana + * arch/etrax100/kernel/fasttimer.c v1.8 from the elinux tree. + * + * Revision 1.8 2001/11/27 13:50:40 pkj + * Disable interrupts while stopping the timer and while modifying the + * list of active timers in timer1_handler() as it may be interrupted + * by other interrupts (e.g., the serial interrupt) which may add fast + * timers. + * + * Revision 1.7 2001/11/22 11:50:32 pkj + * * Only store information about the last 16 timers. + * * proc_fasttimer_read() now uses an allocated buffer, since it + * requires more space than just a page even for only writing the + * last 16 timers. The buffer is only allocated on request, so + * unless /proc/fasttimer is read, it is never allocated. + * * Renamed fast_timer_started to fast_timers_started to match + * fast_timers_added and fast_timers_expired. + * * Some clean-up. + * + * Revision 1.6 2000/12/13 14:02:08 johana + * Removed volatile for fast_timer_list + * + * Revision 1.5 2000/12/13 13:55:35 johana + * Added DEBUG_LOG, added som cli() and cleanup + * + * Revision 1.4 2000/12/05 13:48:50 johana + * Added range check when writing proc file, modified timer int handling + * + * Revision 1.3 2000/11/23 10:10:20 johana + * More debug/logging possibilities. + * Moved GET_JIFFIES_USEC() to timex.h and time.c + * + * Revision 1.2 2000/11/01 13:41:04 johana + * Clean up and bugfixes. + * Created new do_gettimeofday_fast() that gets a timeval struct + * with time based on jiffies and *R_TIMER0_DATA, uses a table + * for fast conversion of timer value to microseconds. + * (Much faster the standard do_gettimeofday() and we don't really + * wan't to use the true time - we wan't the "uptime" so timers don't screw up + * when we change the time. + * TODO: Add efficient support for continuous timers as well. + * + * Revision 1.1 2000/10/26 15:49:16 johana + * Added fasttimer, highresolution timers. + * + * Copyright (C) 2000,2001 2002 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 <linux/config.h> +#include <linux/version.h> + +#include <asm/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 +#define SANITYCHECK(x) x +static int sanity_failed = 0; +#else +#define SANITYCHECK(x) +#endif + +#define D1(x) +#define D2(x) +#define DP(x) + +#define __INLINE__ inline + +static int fast_timer_running = 0; +static int fast_timers_added = 0; +static int fast_timers_started = 0; +static int fast_timers_expired = 0; +static int fast_timers_deleted = 0; +static int fast_timer_is_init = 0; +static int fast_timer_ints = 0; + +struct fast_timer *fast_timer_list = NULL; + +#ifdef DEBUG_LOG_INCLUDED +#define DEBUG_LOG_MAX 128 +static const char * debug_log_string[DEBUG_LOG_MAX]; +static unsigned long debug_log_value[DEBUG_LOG_MAX]; +static int debug_log_cnt = 0; +static int debug_log_cnt_wrapped = 0; + +#define DEBUG_LOG(string, value) \ +{ \ + unsigned long log_flags; \ + save_flags(log_flags); \ + cli(); \ + 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; \ + } \ + restore_flags(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 */ +void __INLINE__ do_gettimeofday_fast(struct timeval *tv) +{ + unsigned long sec = jiffies; + unsigned long usec = GET_JIFFIES_USEC(); + + usec += (sec % HZ) * (1000000 / HZ); + sec = sec / HZ; + + if (usec > 1000000) + { + usec -= 1000000; + sec++; + } + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +int __INLINE__ timeval_cmp(struct timeval *t0, struct timeval *t1) +{ + if (t0->tv_sec < t1->tv_sec) + { + return -1; + } + else if (t0->tv_sec > t1->tv_sec) + { + return 1; + } + if (t0->tv_usec < t1->tv_usec) + { + return -1; + } + else if (t0->tv_usec > t1->tv_usec) + { + return 1; + } + return 0; +} + +void __INLINE__ 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("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)); + + save_flags(flags); + cli(); + + do_gettimeofday_fast(&t->tv_set); + tmp = fast_timer_list; + + SANITYCHECK({ /* Check so this is not in the list already... */ + while (tmp != NULL) + { + if (tmp == t) + { + printk(KERN_WARNING + "timer name: %s data: 0x%08lX already in list!\n", name, data); + sanity_failed++; + return; + } + else + { + tmp = tmp->next; + } + } + tmp = fast_timer_list; + }); + + t->delay_us = delay_us; + t->function = function; + t->data = data; + t->name = name; + + t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000; + t->tv_expires.tv_sec = t->tv_set.tv_sec + delay_us / 1000000; + if (t->tv_expires.tv_usec > 1000000) + { + t->tv_expires.tv_usec -= 1000000; + t->tv_expires.tv_sec++; + } +#ifdef FAST_TIMER_LOG + timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t; +#endif + fast_timers_added++; + + /* Check if this should timeout before anything else */ + if (tmp == NULL || timeval_cmp(&t->tv_expires, &tmp->tv_expires) < 0) + { + /* Put first in list and modify the timer value */ + t->prev = NULL; + t->next = fast_timer_list; + if (fast_timer_list) + { + fast_timer_list->prev = t; + } + fast_timer_list = t; +#ifdef FAST_TIMER_LOG + timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t; +#endif + start_timer1(delay_us); + } else { + /* Put in correct place in list */ + while (tmp->next && + timeval_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0) + { + tmp = tmp->next; + } + /* Insert t after tmp */ + t->prev = tmp; + t->next = tmp->next; + if (tmp->next) + { + tmp->next->prev = t; + } + tmp->next = t; + } + + D2(printk("start_one_shot_timer: %d us done\n", delay_us)); + + restore_flags(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; + + save_flags(flags); + cli(); + ret = detach_fast_timer(t); + t->next = t->prev = NULL; + restore_flags(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 pt_regs *regs) +{ + struct fast_timer *t; + unsigned long flags; + + save_flags(flags); + cli(); + + /* 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++; + + restore_flags(flags); + + t = fast_timer_list; + while (t) + { + struct timeval tv; + + /* Has it really expired? */ + do_gettimeofday_fast(&tv); + D1(printk("t: %is %06ius\n", tv.tv_sec, tv.tv_usec)); + + if (timeval_cmp(&t->tv_expires, &tv) <= 0) + { + /* Yes it has expired */ +#ifdef FAST_TIMER_LOG + timer_expired_log[fast_timers_expired % NUM_TIMER_STATS] = *t; +#endif + fast_timers_expired++; + + /* Remove this timer before call, since it may reuse the timer */ + save_flags(flags); + cli(); + 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; + restore_flags(flags); + + if (t->function != NULL) + { + t->function(t->data); + } + 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")); + } + + save_flags(flags); + cli(); + if ((t = fast_timer_list) != NULL) + { + /* Start next timer.. */ + long us; + struct timeval tv; + + do_gettimeofday_fast(&tv); + us = ((t->tv_expires.tv_sec - tv.tv_sec) * 1000000 + + t->tv_expires.tv_usec - tv.tv_usec); + if (us > 0) + { + if (!fast_timer_running) + { +#ifdef FAST_TIMER_LOG + timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t; +#endif + start_timer1(us); + } + restore_flags(flags); + break; + } + else + { + /* Timer already expired, let's handle it better late than never. + * The normal loop handles it + */ + D1(printk("e! %d\n", us)); + } + } + restore_flags(flags); + } + + if (!t) + { + D1(printk("t1 stop!\n")); + } + + return IRQ_HANDLED; +} + +static void wake_up_func(unsigned long data) +{ +#ifdef DECLARE_WAITQUEUE + wait_queue_head_t *sleep_wait_p = (wait_queue_head_t*)data; +#else + struct wait_queue **sleep_wait_p = (struct wait_queue **)data; +#endif + wake_up(sleep_wait_p); +} + + +/* Useful API */ + +void schedule_usleep(unsigned long us) +{ + struct fast_timer t; +#ifdef DECLARE_WAITQUEUE + wait_queue_head_t sleep_wait; + init_waitqueue_head(&sleep_wait); + { + DECLARE_WAITQUEUE(wait, current); +#else + struct wait_queue *sleep_wait = NULL; + struct wait_queue wait = { current, NULL }; +#endif + + D1(printk("schedule_usleep(%d)\n", us)); + add_wait_queue(&sleep_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us, + "usleep"); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&sleep_wait, &wait); + D1(printk("done schedule_usleep(%d)\n", us)); +#ifdef DECLARE_WAITQUEUE + } +#endif +} + +#ifdef CONFIG_PROC_FS +static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len + ,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 timeval tv; + struct fast_timer *t, *nextt; + static char *bigbuf = NULL; + static unsigned long used; + + if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE))) + { + used = 0; + bigbuf[0] = '\0'; + return 0; + } + + if (!offset || !used) + { + do_gettimeofday_fast(&tv); + + used = 0; + used += sprintf(bigbuf + used, "Fast timers added: %i\n", + fast_timers_added); + used += sprintf(bigbuf + used, "Fast timers started: %i\n", + fast_timers_started); + used += sprintf(bigbuf + used, "Fast timer interrupts: %i\n", + fast_timer_ints); + used += sprintf(bigbuf + used, "Fast timers expired: %i\n", + fast_timers_expired); + used += sprintf(bigbuf + used, "Fast timers deleted: %i\n", + fast_timers_deleted); + used += sprintf(bigbuf + used, "Fast timer running: %s\n", + fast_timer_running ? "yes" : "no"); + used += sprintf(bigbuf + used, "Current time: %lu.%06lu\n", + (unsigned long)tv.tv_sec, + (unsigned long)tv.tv_usec); +#ifdef FAST_TIMER_SANITY_CHECKS + used += sprintf(bigbuf + used, "Sanity failed: %i\n", + sanity_failed); +#endif + used += sprintf(bigbuf + used, "\n"); + +#ifdef DEBUG_LOG_INCLUDED + { + int end_i = debug_log_cnt; + i = 0; + + if (debug_log_cnt_wrapped) + { + i = debug_log_cnt; + } + + while ((i != end_i || (debug_log_cnt_wrapped && !used)) && + used+100 < BIG_BUF_SIZE) + { + used += sprintf(bigbuf + used, debug_log_string[i], + debug_log_value[i]); + i = (i+1) % DEBUG_LOG_MAX; + } + } + used += sprintf(bigbuf + used, "\n"); +#endif + + num_to_show = (fast_timers_started < NUM_TIMER_STATS ? fast_timers_started: + NUM_TIMER_STATS); + used += sprintf(bigbuf + used, "Timers started: %i\n", fast_timers_started); + for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE) ; i++) + { + int cur = (fast_timers_started - i - 1) % NUM_TIMER_STATS; + +#if 1 //ndef FAST_TIMER_LOG + used += sprintf(bigbuf + used, "div: %i 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_sec, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_usec, + t->delay_us, + t->data + ); +#endif + } + used += sprintf(bigbuf + used, "\n"); + +#ifdef FAST_TIMER_LOG + num_to_show = (fast_timers_added < NUM_TIMER_STATS ? fast_timers_added: + NUM_TIMER_STATS); + used += sprintf(bigbuf + used, "Timers added: %i\n", fast_timers_added); + for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE); i++) + { + t = &timer_added_log[(fast_timers_added - i - 1) % NUM_TIMER_STATS]; + used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " + "d: %6li us data: 0x%08lX" + "\n", + t->name, + (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_usec, + t->delay_us, + t->data + ); + } + used += sprintf(bigbuf + used, "\n"); + + num_to_show = (fast_timers_expired < NUM_TIMER_STATS ? fast_timers_expired: + NUM_TIMER_STATS); + used += sprintf(bigbuf + used, "Timers expired: %i\n", fast_timers_expired); + for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE); i++) + { + t = &timer_expired_log[(fast_timers_expired - i - 1) % NUM_TIMER_STATS]; + used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " + "d: %6li us data: 0x%08lX" + "\n", + t->name, + (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_usec, + t->delay_us, + t->data + ); + } + used += sprintf(bigbuf + used, "\n"); +#endif + + used += sprintf(bigbuf + used, "Active timers:\n"); + save_flags(flags); + cli(); + t = fast_timer_list; + while (t != NULL && (used+100 < BIG_BUF_SIZE)) + { + nextt = t->next; + restore_flags(flags); + used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " + "d: %6li us data: 0x%08lX" +/* " func: 0x%08lX" */ + "\n", + t->name, + (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_usec, + t->delay_us, + t->data +/* , t->function */ + ); + cli(); + if (t->next != nextt) + { + printk(KERN_WARNING "timer removed!\n"); + } + t = nextt; + } + restore_flags(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 timeval tv_exp[100]; + +static void test_timeout(unsigned long data) +{ + do_gettimeofday_fast(&tv_exp[data]); + exp_num[data] = num_test_timeout; + + num_test_timeout++; +} + +static void test_timeout1(unsigned long data) +{ + do_gettimeofday_fast(&tv_exp[data]); + exp_num[data] = num_test_timeout; + if (data < 7) + { + start_one_shot_timer(&tr[i], test_timeout1, i, 1000, "timeout1"); + i++; + } + num_test_timeout++; +} + +DP( +static char buf0[2000]; +static char buf1[2000]; +static char buf2[2000]; +static char buf3[2000]; +static char buf4[2000]; +); + +static char buf5[6000]; +static int j_u[1000]; + +static void fast_timer_test(void) +{ + int prev_num; + int j; + + struct timeval tv, tv0, tv1, tv2; + + printk("fast_timer_test() start\n"); + do_gettimeofday_fast(&tv); + + for (j = 0; j < 1000; j++) + { + j_u[j] = GET_JIFFIES_USEC(); + } + for (j = 0; j < 100; j++) + { + do_gettimeofday_fast(&tv_exp[j]); + } + printk("fast_timer_test() %is %06i\n", tv.tv_sec, tv.tv_usec); + + for (j = 0; j < 1000; j++) + { + printk("%i %i %i %i %i\n",j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]); + j += 4; + } + for (j = 0; j < 100; j++) + { + printk("%i.%i %i.%i %i.%i %i.%i %i.%i\n", + tv_exp[j].tv_sec,tv_exp[j].tv_usec, + tv_exp[j+1].tv_sec,tv_exp[j+1].tv_usec, + tv_exp[j+2].tv_sec,tv_exp[j+2].tv_usec, + tv_exp[j+3].tv_sec,tv_exp[j+3].tv_usec, + tv_exp[j+4].tv_sec,tv_exp[j+4].tv_usec); + j += 4; + } + do_gettimeofday_fast(&tv0); + start_one_shot_timer(&tr[i], test_timeout, i, 50000, "test0"); + DP(proc_fasttimer_read(buf0, NULL, 0, 0, 0)); + i++; + start_one_shot_timer(&tr[i], test_timeout, i, 70000, "test1"); + DP(proc_fasttimer_read(buf1, NULL, 0, 0, 0)); + i++; + start_one_shot_timer(&tr[i], test_timeout, i, 40000, "test2"); + DP(proc_fasttimer_read(buf2, NULL, 0, 0, 0)); + i++; + start_one_shot_timer(&tr[i], test_timeout, i, 60000, "test3"); + DP(proc_fasttimer_read(buf3, NULL, 0, 0, 0)); + i++; + start_one_shot_timer(&tr[i], test_timeout1, i, 55000, "test4xx"); + DP(proc_fasttimer_read(buf4, NULL, 0, 0, 0)); + i++; + do_gettimeofday_fast(&tv1); + + proc_fasttimer_read(buf5, NULL, 0, 0, 0); + + prev_num = num_test_timeout; + while (num_test_timeout < i) + { + if (num_test_timeout != prev_num) + { + prev_num = num_test_timeout; + } + } + do_gettimeofday_fast(&tv2); + printk("Timers started %is %06i\n", tv0.tv_sec, tv0.tv_usec); + printk("Timers started at %is %06i\n", tv1.tv_sec, tv1.tv_usec); + printk("Timers done %is %06i\n", tv2.tv_sec, tv2.tv_usec); + DP(printk("buf0:\n"); + printk(buf0); + printk("buf1:\n"); + printk(buf1); + printk("buf2:\n"); + printk(buf2); + printk("buf3:\n"); + printk(buf3); + printk("buf4:\n"); + printk(buf4); + ); + printk("buf5:\n"); + printk(buf5); + + printk("timers set:\n"); + for(j = 0; j<i; j++) + { + struct fast_timer *t = &tr[j]; + printk("%-10s set: %6is %06ius exp: %6is %06ius " + "data: 0x%08X func: 0x%08X\n", + t->name, + t->tv_set.tv_sec, + t->tv_set.tv_usec, + t->tv_expires.tv_sec, + t->tv_expires.tv_usec, + t->data, + t->function + ); + + printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n", + t->delay_us, + tv_exp[j].tv_sec, + tv_exp[j].tv_usec, + exp_num[j], + (tv_exp[j].tv_sec - t->tv_expires.tv_sec)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec); + } + proc_fasttimer_read(buf5, NULL, 0, 0, 0); + printk("buf5 after all done:\n"); + printk(buf5); + printk("fast_timer_test() done\n"); +} +#endif + + +void fast_timer_init(void) +{ + /* For some reason, request_irq() hangs when called froom time_init() */ + if (!fast_timer_is_init) + { +#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, SA_SHIRQ, + "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 + } +} diff --git a/arch/cris/arch-v10/kernel/head.S b/arch/cris/arch-v10/kernel/head.S new file mode 100644 index 00000000000..2c1dd1184a8 --- /dev/null +++ b/arch/cris/arch-v10/kernel/head.S @@ -0,0 +1,882 @@ +/* $Id: head.S,v 1.7 2004/05/14 07:58:01 starvik Exp $ + * + * Head of the kernel - alter with care + * + * Copyright (C) 2000, 2001 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + * $Log: head.S,v $ + * Revision 1.7 2004/05/14 07:58:01 starvik + * Merge of changes from 2.4 + * + * Revision 1.6 2003/04/28 05:31:46 starvik + * Added section attributes + * + * Revision 1.5 2002/12/11 15:42:02 starvik + * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/*.c + * + * Revision 1.4 2002/11/07 09:00:44 starvik + * Names changed for init sections + * init_task_union -> init_thread_union + * + * Revision 1.3 2002/02/05 15:38:23 bjornw + * Oops.. non-CRAMFS_MAGIC should jump over the copying, not into it... + * + * Revision 1.2 2001/12/18 13:35:19 bjornw + * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). + * + * Revision 1.43 2001/11/08 15:09:43 starvik + * Only start MII clock if Ethernet is configured + * + * Revision 1.42 2001/11/08 14:37:34 starvik + * Start MII clock early to make sure that it is running at tranceiver reset + * + * Revision 1.41 2001/10/29 14:55:58 pkj + * Corrected pa$r0 to par0. + * + * Revision 1.40 2001/10/03 14:59:57 pkj + * Added support for resetting the Bluetooth hardware. + * + * Revision 1.39 2001/10/01 14:45:03 bjornw + * Removed underscores and added register prefixes + * + * Revision 1.38 2001/09/21 07:14:11 jonashg + * Made root filesystem (cramfs) use mtdblock driver when booting from flash. + * + * Revision 1.37 2001/09/11 13:44:29 orjanf + * Decouple usage of serial ports for debug and kgdb. + * + * Revision 1.36 2001/06/29 12:39:31 pkj + * Added support for mirroring the first flash to just below the + * second one, to make them look consecutive to cramfs. + * + * Revision 1.35 2001/06/25 14:07:00 hp + * Fix review comment. + * * head.S: Use IO_STATE, IO_FIELD and IO_MASK constructs instead of + * magic numbers. Add comment that -traditional must not be used. + * * entry.S (SYMBOL_NAME): Change redefinition to use ## concatenation. + * Correct and update comment. + * * Makefile (.S.o): Don't use -traditional. Add comment why the + * toplevel rule can't be used (now that there's a reason). + * + * Revision 1.34 2001/05/15 07:08:14 hp + * Tweak "notice" to reflect that both r8 r9 are used + * + * Revision 1.33 2001/05/15 06:40:05 hp + * Put bulk of code in .text.init, data in .data.init + * + * Revision 1.32 2001/05/15 06:18:56 hp + * Execute review comment: s/bcc/bhs/g; s/bcs/blo/g + * + * Revision 1.31 2001/05/15 06:08:40 hp + * Add sentence about autodetecting the bit31-MMU-bug + * + * Revision 1.30 2001/05/15 06:00:05 hp + * Update comment: LOW_MAP is not forced on xsim anymore. + * + * Revision 1.29 2001/04/18 12:51:59 orjanf + * * Reverted review change regarding the use of bcs/bcc. + * * Removed non-working LED-clearing code. + * + * Revision 1.28 2001/04/17 13:58:39 orjanf + * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. + * + * Revision 1.27 2001/04/17 11:42:35 orjanf + * Changed according to review: + * * Added comment explaining memory map bug. + * * Changed bcs and bcc to blo and bhs, respectively. + * * Removed mentioning of Stallone and Olga boards. + * + * Revision 1.26 2001/04/06 12:31:07 jonashg + * Check for cramfs in flash before RAM instead of RAM before flash. + * + * Revision 1.25 2001/04/04 06:23:53 starvik + * Initialize DRAM if not already initialized + * + * Revision 1.24 2001/04/03 11:12:00 starvik + * Removed dram init (done by rescue or etrax100boot + * Corrected include + * + * Revision 1.23 2001/04/03 09:53:03 starvik + * Include hw_settings.S + * + * Revision 1.22 2001/03/26 14:23:26 bjornw + * Namechange of some config options + * + * Revision 1.21 2001/03/08 12:14:41 bjornw + * * Config name for ETRAX IDE was renamed + * * Removed G27 auto-setting when JULIETTE is chosen (need to make this + * a new config option later) + * + * Revision 1.20 2001/02/23 12:47:56 bjornw + * MMU regs during LOW_MAP updated to reflect a newer reality + * + * Revision 1.19 2001/02/19 11:12:07 bjornw + * Changed comment header format + * + * Revision 1.18 2001/02/15 07:25:38 starvik + * Added support for synchronous serial ports + * + * Revision 1.17 2001/02/08 15:53:13 starvik + * Last commit removed some important ifdefs + * + * Revision 1.16 2001/02/08 15:20:38 starvik + * Include dram_init.S as inline + * + * Revision 1.15 2001/01/29 18:12:01 bjornw + * Corrected some comments + * + * Revision 1.14 2001/01/29 13:11:29 starvik + * Include dram_init.S (with DRAM/SDRAM initialization) + * + * Revision 1.13 2001/01/23 14:54:57 markusl + * Updated for USB + * i.e. added r_gen_config settings + * + * Revision 1.12 2001/01/19 16:16:29 perf + * Added temporary mapping of 0x0c->0x0c to avoid flash loading confusion. + * Renamed serial options from ETRAX100 to ETRAX. + * + * Revision 1.11 2001/01/16 16:31:38 bjornw + * * Changed name and semantics of running_from_flash to romfs_in_flash, + * set by head.S to indicate to setup.c whether there is a cramfs image + * after the kernels BSS or not. Should work for all three boot-cases + * (DRAM with cramfs in DRAM, DRAM with cramfs in flash (compressed boot), + * and flash with cramfs in flash) + * + * Revision 1.10 2001/01/16 14:12:21 bjornw + * * Check for cramfs start passed in r9 from the decompressor, if all other + * cramfs options fail (if we boot from DRAM but don't find a cramfs image + * after the kernel in DRAM, it is probably still in the flash) + * * Check magic in cramfs detection when booting from flash directly + * + * Revision 1.9 2001/01/15 17:17:02 bjornw + * * Corrected the code that detects the cramfs lengths + * * Added a comment saying that the above does not work due to other + * reasons.. + * + * Revision 1.8 2001/01/15 16:27:51 jonashg + * Made boot after flashing work. + * * end destination is __vmlinux_end in RAM. + * * _romfs_start moved because of virtual memory. + * + * Revision 1.7 2000/11/21 13:55:29 bjornw + * Use CONFIG_CRIS_LOW_MAP for the low VM map instead of explicit CPU type + * + * Revision 1.6 2000/10/06 12:36:55 bjornw + * Forgot swapper_pg_dir when changing memory map.. + * + * Revision 1.5 2000/10/04 16:49:30 bjornw + * * Fixed memory mapping in LX + * * Check for cramfs instead of romfs + * + */ + +#include <linux/config.h> +#define ASSEMBLER_MACROS_ONLY +/* The IO_* macros use the ## token concatenation operator, so + -traditional must not be used when assembling this file. */ +#include <asm/arch/sv_addr_ag.h> + +#define CRAMFS_MAGIC 0x28cd3d45 +#define RAM_INIT_MAGIC 0x56902387 + +#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 __vmlinux_end, $r0; the image will be after the vmlinux end address + 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: + + ;; 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 +#if (!defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT0)) \ + && !defined(CONFIG_DMA_MEMCPY) + ; DMA channels 6 and 7 to ser0, kgdb doesnt want DMA + or.d IO_STATE (R_GEN_CONFIG, dma7, serial0) \ + | IO_STATE (R_GEN_CONFIG, dma6, serial0),$r0 +#endif +#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT1) + ; DMA channels 8 and 9 to ser1, kgdb doesnt want DMA + or.d IO_STATE (R_GEN_CONFIG, dma9, serial1) \ + | IO_STATE (R_GEN_CONFIG, dma8, serial1),$r0 +#endif +#ifdef CONFIG_DMA_MEMCPY + ; 6/7 memory-memory DMA + or.d IO_STATE (R_GEN_CONFIG, dma7, intdma6) \ + | IO_STATE (R_GEN_CONFIG, dma6, intdma7),$r0 +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + ; Enable serial port 2 + or.w IO_STATE (R_GEN_CONFIG, ser2, select),$r0 +#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT2) + ; DMA channels 2 and 3 to ser2, kgdb doesnt want DMA + or.d IO_STATE (R_GEN_CONFIG, dma3, serial2) \ + | IO_STATE (R_GEN_CONFIG, dma2, serial2),$r0 +#endif +#endif +#if defined(CONFIG_ETRAX_SERIAL_PORT3) || defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + ; Enable serial port 3 + or.w IO_STATE (R_GEN_CONFIG, ser3, select),$r0 +#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT3) + ; DMA channels 4 and 5 to ser3, kgdb doesnt want DMA + or.d IO_STATE (R_GEN_CONFIG, dma5, serial3) \ + | IO_STATE (R_GEN_CONFIG, dma4, serial3),$r0 +#endif +#endif +#if defined(CONFIG_ETRAX_PARALLEL_PORT0) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE) + ; parport 0 enabled using DMA 2/3 + or.w IO_STATE (R_GEN_CONFIG, par0, select),$r0 +#endif +#if defined(CONFIG_ETRAX_PARALLEL_PORT1) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE) + ; parport 1 enabled using DMA 4/5 + or.w IO_STATE (R_GEN_CONFIG, par1, select),$r0 +#endif +#ifdef CONFIG_ETRAX_IDE + ; DMA channels 2 and 3 to ATA, ATA enabled + or.d IO_STATE (R_GEN_CONFIG, dma3, ata) \ + | IO_STATE (R_GEN_CONFIG, dma2, ata) \ + | IO_STATE (R_GEN_CONFIG, ata, select),$r0 +#endif + +#ifdef CONFIG_ETRAX_USB_HOST_PORT1 + ; Set the USB port 1 enable bit + or.d IO_STATE (R_GEN_CONFIG, usb1, select),$r0 +#endif +#ifdef CONFIG_ETRAX_USB_HOST_PORT2 + ; Set the USB port 2 enable bit + or.d IO_STATE (R_GEN_CONFIG, usb2, select),$r0 +#endif +#ifdef CONFIG_ETRAX_USB_HOST + ; Connect DMA channels 8 and 9 to USB + and.d (~(IO_MASK (R_GEN_CONFIG, dma9) \ + | IO_MASK (R_GEN_CONFIG, dma8))) \ + | IO_STATE (R_GEN_CONFIG, dma9, usb) \ + | IO_STATE (R_GEN_CONFIG, dma8, usb),$r0 +#endif + +#ifdef CONFIG_JULIETTE + ; DMA channels 4 and 5 to EXTDMA0, for Juliette + or.d IO_STATE (R_GEN_CONFIG, dma5, extdma0) \ + | IO_STATE (R_GEN_CONFIG, dma4, extdma0),$r0 +#endif + +#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_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" diff --git a/arch/cris/arch-v10/kernel/irq.c b/arch/cris/arch-v10/kernel/irq.c new file mode 100644 index 00000000000..b2f16d6fc87 --- /dev/null +++ b/arch/cris/arch-v10/kernel/irq.c @@ -0,0 +1,204 @@ +/* $Id: irq.c,v 1.2 2004/06/09 05:30:27 starvik Exp $ + * + * 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 <linux/kernel.h> +#include <linux/init.h> +#include <linux/config.h> + +irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */ + +/* 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) +BUILD_IRQ(17, 0x20000) +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 (*bad_interrupt[NR_IRQS])(void) = { + NULL, NULL, + NULL, bad_IRQ3_interrupt, + bad_IRQ4_interrupt, bad_IRQ5_interrupt, + bad_IRQ6_interrupt, bad_IRQ7_interrupt, + bad_IRQ8_interrupt, bad_IRQ9_interrupt, + bad_IRQ10_interrupt, bad_IRQ11_interrupt, + bad_IRQ12_interrupt, bad_IRQ13_interrupt, + NULL, NULL, + bad_IRQ16_interrupt, bad_IRQ17_interrupt, + bad_IRQ18_interrupt, bad_IRQ19_interrupt, + bad_IRQ20_interrupt, bad_IRQ21_interrupt, + bad_IRQ22_interrupt, bad_IRQ23_interrupt, + bad_IRQ24_interrupt, bad_IRQ25_interrupt, + NULL, NULL, NULL, NULL, NULL, + bad_IRQ31_interrupt +}; + +void arch_setup_irq(int irq) +{ + set_int_vector(irq, interrupt[irq]); +} + +void arch_free_irq(int irq) +{ + set_int_vector(irq, bad_interrupt[irq]); +} + +void weird_irq(void); +void system_call(void); /* from entry.S */ +void do_sigtrap(void); /* from entry.S */ +void gdb_handle_breakpoint(void); /* from entry.S */ + +/* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and + setting the irq vector table to point to bad_interrupt ptrs. +*/ + +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; + + /* clear the shortcut entry points */ + + for(i = 0; i < NR_IRQS; i++) + irq_shortcuts[i] = NULL; + + for (i = 0; i < 256; i++) + etrax_irv->v[i] = weird_irq; + + /* 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); + + /* set all etrax irq's to the bad handlers */ + for (i = 2; i < NR_IRQS; i++) + set_int_vector(i, bad_interrupt[i]); + + /* except IRQ 15 which is the multiple-IRQ handler on Etrax100 */ + + set_int_vector(15, multiple_interrupt); + + /* 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 +} diff --git a/arch/cris/arch-v10/kernel/kgdb.c b/arch/cris/arch-v10/kernel/kgdb.c new file mode 100644 index 00000000000..7d368c877ee --- /dev/null +++ b/arch/cris/arch-v10/kernel/kgdb.c @@ -0,0 +1,1568 @@ +/*!************************************************************************** +*! +*! FILE NAME : kgdb.c +*! +*! DESCRIPTION: Implementation of the gdb stub with respect to ETRAX 100. +*! It is a mix of arch/m68k/kernel/kgdb.c and cris_stub.c. +*! +*!--------------------------------------------------------------------------- +*! HISTORY +*! +*! DATE NAME CHANGES +*! ---- ---- ------- +*! Apr 26 1999 Hendrik Ruijter Initial version. +*! May 6 1999 Hendrik Ruijter Removed call to strlen in libc and removed +*! struct assignment as it generates calls to +*! memcpy in libc. +*! Jun 17 1999 Hendrik Ruijter Added gdb 4.18 support. 'X', 'qC' and 'qL'. +*! Jul 21 1999 Bjorn Wesen eLinux port +*! +*! $Log: kgdb.c,v $ +*! Revision 1.5 2004/10/07 13:59:08 starvik +*! Corrected call to set_int_vector +*! +*! Revision 1.4 2003/04/09 05:20:44 starvik +*! Merge of Linux 2.5.67 +*! +*! Revision 1.3 2003/01/21 19:11:08 starvik +*! Modified include path for new dir layout +*! +*! Revision 1.2 2002/11/19 14:35:24 starvik +*! Changes from linux 2.4 +*! Changed struct initializer syntax to the currently prefered notation +*! +*! Revision 1.1 2001/12/17 13:59:27 bjornw +*! Initial revision +*! +*! Revision 1.6 2001/10/09 13:10:03 matsfg +*! Added $ on registers and removed some underscores +*! +*! Revision 1.5 2001/04/17 13:58:39 orjanf +*! * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. +*! +*! Revision 1.4 2001/02/23 13:45:19 bjornw +*! config.h check +*! +*! Revision 1.3 2001/01/31 18:08:23 orjanf +*! Removed kgdb_handle_breakpoint from being the break 8 handler. +*! +*! Revision 1.2 2001/01/12 14:22:25 orjanf +*! Updated kernel debugging support to work with ETRAX 100LX. +*! +*! Revision 1.1 2000/07/10 16:25:21 bjornw +*! Initial revision +*! +*! Revision 1.1.1.1 1999/12/03 14:57:31 bjornw +*! * Initial version of arch/cris, the latest CRIS architecture with an MMU. +*! Mostly copied from arch/etrax100 with appropriate renames of files. +*! The mm/ subdir is copied from arch/i386. +*! This does not compile yet at all. +*! +*! +*! Revision 1.4 1999/07/22 17:25:25 bjornw +*! Dont wait for + in putpacket if we havent hit the initial breakpoint yet. Added a kgdb_init function which sets up the break and irq vectors. +*! +*! Revision 1.3 1999/07/21 19:51:18 bjornw +*! Check if the interrupting char is a ctrl-C, ignore otherwise. +*! +*! Revision 1.2 1999/07/21 18:09:39 bjornw +*! Ported to eLinux architecture, and added some kgdb documentation. +*! +*! +*!--------------------------------------------------------------------------- +*! +*! $Id: kgdb.c,v 1.5 2004/10/07 13:59:08 starvik Exp $ +*! +*! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN +*! +*!**************************************************************************/ +/* @(#) cris_stub.c 1.3 06/17/99 */ + +/* + * kgdb usage notes: + * ----------------- + * + * If you select CONFIG_ETRAX_KGDB in the configuration, the kernel will be + * built with different gcc flags: "-g" is added to get debug infos, and + * "-fomit-frame-pointer" is omitted to make debugging easier. Since the + * resulting kernel will be quite big (approx. > 7 MB), it will be stripped + * before compresion. Such a kernel will behave just as usually, except if + * given a "debug=<device>" command line option. (Only serial devices are + * allowed for <device>, i.e. no printers or the like; possible values are + * machine depedend and are the same as for the usual debug device, the one + * for logging kernel messages.) If that option is given and the device can be + * initialized, the kernel will connect to the remote gdb in trap_init(). The + * serial parameters are fixed to 8N1 and 115200 bps, for easyness of + * implementation. + * + * To start a debugging session, start that gdb with the debugging kernel + * image (the one with the symbols, vmlinux.debug) named on the command line. + * This file will be used by gdb to get symbol and debugging infos about the + * kernel. Next, select remote debug mode by + * target remote <device> + * where <device> is the name of the serial device over which the debugged + * machine is connected. Maybe you have to adjust the baud rate by + * set remotebaud <rate> + * or also other parameters with stty: + * shell stty ... </dev/... + * If the kernel to debug has already booted, it waited for gdb and now + * connects, and you'll see a breakpoint being reported. If the kernel isn't + * running yet, start it now. The order of gdb and the kernel doesn't matter. + * Another thing worth knowing about in the getting-started phase is how to + * debug the remote protocol itself. This is activated with + * set remotedebug 1 + * gdb will then print out each packet sent or received. You'll also get some + * messages about the gdb stub on the console of the debugged machine. + * + * If all that works, you can use lots of the usual debugging techniques on + * the kernel, e.g. inspecting and changing variables/memory, setting + * breakpoints, single stepping and so on. It's also possible to interrupt the + * debugged kernel by pressing C-c in gdb. Have fun! :-) + * + * The gdb stub is entered (and thus the remote gdb gets control) in the + * following situations: + * + * - If breakpoint() is called. This is just after kgdb initialization, or if + * a breakpoint() call has been put somewhere into the kernel source. + * (Breakpoints can of course also be set the usual way in gdb.) + * In eLinux, we call breakpoint() in init/main.c after IRQ initialization. + * + * - If there is a kernel exception, i.e. bad_super_trap() or die_if_kernel() + * are entered. All the CPU exceptions are mapped to (more or less..., see + * the hard_trap_info array below) appropriate signal, which are reported + * to gdb. die_if_kernel() is usually called after some kind of access + * error and thus is reported as SIGSEGV. + * + * - When panic() is called. This is reported as SIGABRT. + * + * - If C-c is received over the serial line, which is treated as + * SIGINT. + * + * Of course, all these signals are just faked for gdb, since there is no + * signal concept as such for the kernel. It also isn't possible --obviously-- + * to set signal handlers from inside gdb, or restart the kernel with a + * signal. + * + * Current limitations: + * + * - While the kernel is stopped, interrupts are disabled for safety reasons + * (i.e., variables not changing magically or the like). But this also + * means that the clock isn't running anymore, and that interrupts from the + * hardware may get lost/not be served in time. This can cause some device + * errors... + * + * - When single-stepping, only one instruction of the current thread is + * executed, but interrupts are allowed for that time and will be serviced + * if pending. Be prepared for that. + * + * - All debugging happens in kernel virtual address space. There's no way to + * access physical memory not mapped in kernel space, or to access user + * space. A way to work around this is using get_user_long & Co. in gdb + * expressions, but only for the current process. + * + * - Interrupting the kernel only works if interrupts are currently allowed, + * and the interrupt of the serial line isn't blocked by some other means + * (IPL too high, disabled, ...) + * + * - The gdb stub is currently not reentrant, i.e. errors that happen therein + * (e.g. accessing invalid memory) may not be caught correctly. This could + * be removed in future by introducing a stack of struct registers. + * + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to kgdb_init() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + */ + + +#include <linux/string.h> +#include <linux/signal.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/linkage.h> + +#include <asm/setup.h> +#include <asm/ptrace.h> + +#include <asm/arch/svinto.h> +#include <asm/irq.h> + +static int kgdb_started = 0; + +/********************************* Register image ****************************/ +/* Use the order of registers as defined in "AXIS ETRAX CRIS Programmer's + Reference", p. 1-1, with the additional register definitions of the + ETRAX 100LX in cris-opc.h. + There are 16 general 32-bit registers, R0-R15, where R14 is the stack + pointer, SP, and R15 is the program counter, PC. + There are 16 special registers, P0-P15, where three of the unimplemented + registers, P0, P4 and P8, are reserved as zero-registers. A read from + any of these registers returns zero and a write has no effect. */ + +typedef +struct register_image +{ + /* Offset */ + unsigned int r0; /* 0x00 */ + unsigned int r1; /* 0x04 */ + unsigned int r2; /* 0x08 */ + unsigned int r3; /* 0x0C */ + unsigned int r4; /* 0x10 */ + unsigned int r5; /* 0x14 */ + unsigned int r6; /* 0x18 */ + unsigned int r7; /* 0x1C */ + unsigned int r8; /* 0x20 Frame pointer */ + unsigned int r9; /* 0x24 */ + unsigned int r10; /* 0x28 */ + unsigned int r11; /* 0x2C */ + unsigned int r12; /* 0x30 */ + unsigned int r13; /* 0x34 */ + unsigned int sp; /* 0x38 Stack pointer */ + unsigned int pc; /* 0x3C Program counter */ + + unsigned char p0; /* 0x40 8-bit zero-register */ + unsigned char vr; /* 0x41 Version register */ + + unsigned short p4; /* 0x42 16-bit zero-register */ + unsigned short ccr; /* 0x44 Condition code register */ + + unsigned int mof; /* 0x46 Multiply overflow register */ + + unsigned int p8; /* 0x4A 32-bit zero-register */ + unsigned int ibr; /* 0x4E Interrupt base register */ + unsigned int irp; /* 0x52 Interrupt return pointer */ + unsigned int srp; /* 0x56 Subroutine return pointer */ + unsigned int bar; /* 0x5A Breakpoint address register */ + unsigned int dccr; /* 0x5E Double condition code register */ + unsigned int brp; /* 0x62 Breakpoint return pointer (pc in caller) */ + unsigned int usp; /* 0x66 User mode stack pointer */ +} registers; + +/************** Prototypes for local library functions ***********************/ + +/* Copy of strcpy from libc. */ +static char *gdb_cris_strcpy (char *s1, const char *s2); + +/* Copy of strlen from libc. */ +static int gdb_cris_strlen (const char *s); + +/* Copy of memchr from libc. */ +static void *gdb_cris_memchr (const void *s, int c, int n); + +/* Copy of strtol from libc. Does only support base 16. */ +static int gdb_cris_strtol (const char *s, char **endptr, int base); + +/********************** Prototypes for local functions. **********************/ +/* Copy the content of a register image into another. The size n is + the size of the register image. Due to struct assignment generation of + memcpy in libc. */ +static void copy_registers (registers *dptr, registers *sptr, int n); + +/* Copy the stored registers from the stack. Put the register contents + of thread thread_id in the struct reg. */ +static void copy_registers_from_stack (int thread_id, registers *reg); + +/* Copy the registers to the stack. Put the register contents of thread + thread_id from struct reg to the stack. */ +static void copy_registers_to_stack (int thread_id, registers *reg); + +/* Write a value to a specified register regno in the register image + of the current thread. */ +static int write_register (int regno, char *val); + +/* Write a value to a specified register in the stack of a thread other + than the current thread. */ +static write_stack_register (int thread_id, int regno, char *valptr); + +/* Read a value from a specified register in the register image. Returns the + status of the read operation. The register value is returned in valptr. */ +static int read_register (char regno, unsigned int *valptr); + +/* Serial port, reads one character. ETRAX 100 specific. from debugport.c */ +int getDebugChar (void); + +/* Serial port, writes one character. ETRAX 100 specific. from debugport.c */ +void putDebugChar (int val); + +void enableDebugIRQ (void); + +/* Returns the character equivalent of a nibble, bit 7, 6, 5, and 4 of a byte, + represented by int x. */ +static char highhex (int x); + +/* Returns the character equivalent of a nibble, bit 3, 2, 1, and 0 of a byte, + represented by int x. */ +static char lowhex (int x); + +/* Returns the integer equivalent of a hexadecimal character. */ +static int hex (char ch); + +/* Convert the memory, pointed to by mem into hexadecimal representation. + Put the result in buf, and return a pointer to the last character + in buf (null). */ +static char *mem2hex (char *buf, unsigned char *mem, int count); + +/* Convert the array, in hexadecimal representation, pointed to by buf into + binary representation. Put the result in mem, and return a pointer to + the character after the last byte written. */ +static unsigned char *hex2mem (unsigned char *mem, char *buf, int count); + +/* Put the content of the array, in binary representation, pointed to by buf + into memory pointed to by mem, and return a pointer to + the character after the last byte written. */ +static unsigned char *bin2mem (unsigned char *mem, unsigned char *buf, int count); + +/* Await the sequence $<data>#<checksum> and store <data> in the array buffer + returned. */ +static void getpacket (char *buffer); + +/* Send $<data>#<checksum> from the <data> in the array buffer. */ +static void putpacket (char *buffer); + +/* Build and send a response packet in order to inform the host the + stub is stopped. */ +static void stub_is_stopped (int sigval); + +/* All expected commands are sent from remote.c. Send a response according + to the description in remote.c. */ +static void handle_exception (int sigval); + +/* Performs a complete re-start from scratch. ETRAX specific. */ +static void kill_restart (void); + +/******************** Prototypes for global functions. ***********************/ + +/* The string str is prepended with the GDB printout token and sent. */ +void putDebugString (const unsigned char *str, int length); /* used by etrax100ser.c */ + +/* The hook for both static (compiled) and dynamic breakpoints set by GDB. + ETRAX 100 specific. */ +void handle_breakpoint (void); /* used by irq.c */ + +/* The hook for an interrupt generated by GDB. ETRAX 100 specific. */ +void handle_interrupt (void); /* used by irq.c */ + +/* A static breakpoint to be used at startup. */ +void breakpoint (void); /* called by init/main.c */ + +/* From osys_int.c, executing_task contains the number of the current + executing task in osys. Does not know of object-oriented threads. */ +extern unsigned char executing_task; + +/* The number of characters used for a 64 bit thread identifier. */ +#define HEXCHARS_IN_THREAD_ID 16 + +/* Avoid warning as the internal_stack is not used in the C-code. */ +#define USEDVAR(name) { if (name) { ; } } +#define USEDFUN(name) { void (*pf)(void) = (void *)name; USEDVAR(pf) } + +/********************************** Packet I/O ******************************/ +/* BUFMAX defines the maximum number of characters in + inbound/outbound buffers */ +#define BUFMAX 512 + +/* Run-length encoding maximum length. Send 64 at most. */ +#define RUNLENMAX 64 + +/* Definition of all valid hexadecimal characters */ +static const char hexchars[] = "0123456789abcdef"; + +/* The inbound/outbound buffers used in packet I/O */ +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; + +/* Error and warning messages. */ +enum error_type +{ + SUCCESS, E01, E02, E03, E04, E05, E06, E07 +}; +static char *error_message[] = +{ + "", + "E01 Set current or general thread - H[c,g] - internal error.", + "E02 Change register content - P - cannot change read-only register.", + "E03 Thread is not alive.", /* T, not used. */ + "E04 The command is not supported - [s,C,S,!,R,d,r] - internal error.", + "E05 Change register content - P - the register is not implemented..", + "E06 Change memory content - M - internal error.", + "E07 Change register content - P - the register is not stored on the stack" +}; +/********************************* Register image ****************************/ +/* Use the order of registers as defined in "AXIS ETRAX CRIS Programmer's + Reference", p. 1-1, with the additional register definitions of the + ETRAX 100LX in cris-opc.h. + There are 16 general 32-bit registers, R0-R15, where R14 is the stack + pointer, SP, and R15 is the program counter, PC. + There are 16 special registers, P0-P15, where three of the unimplemented + registers, P0, P4 and P8, are reserved as zero-registers. A read from + any of these registers returns zero and a write has no effect. */ +enum register_name +{ + R0, R1, R2, R3, + R4, R5, R6, R7, + R8, R9, R10, R11, + R12, R13, SP, PC, + P0, VR, P2, P3, + P4, CCR, P6, MOF, + P8, IBR, IRP, SRP, + BAR, DCCR, BRP, USP +}; + +/* The register sizes of the registers in register_name. An unimplemented register + is designated by size 0 in this array. */ +static int register_size[] = +{ + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + 1, 1, 0, 0, + 2, 2, 0, 4, + 4, 4, 4, 4, + 4, 4, 4, 4 +}; + +/* Contains the register image of the executing thread in the assembler + part of the code in order to avoid horrible addressing modes. */ +static registers reg; + +/* FIXME: Should this be used? Delete otherwise. */ +/* Contains the assumed consistency state of the register image. Uses the + enum error_type for state information. */ +static int consistency_status = SUCCESS; + +/********************************** Handle exceptions ************************/ +/* The variable reg contains the register image associated with the + current_thread_c variable. It is a complete register image created at + entry. The reg_g contains a register image of a task where the general + registers are taken from the stack and all special registers are taken + from the executing task. It is associated with current_thread_g and used + in order to provide access mainly for 'g', 'G' and 'P'. +*/ + +/* Need two task id pointers in order to handle Hct and Hgt commands. */ +static int current_thread_c = 0; +static int current_thread_g = 0; + +/* Need two register images in order to handle Hct and Hgt commands. The + variable reg_g is in addition to reg above. */ +static registers reg_g; + +/********************************** Breakpoint *******************************/ +/* Use an internal stack in the breakpoint and interrupt response routines */ +#define INTERNAL_STACK_SIZE 1024 +static char internal_stack[INTERNAL_STACK_SIZE]; + +/* Due to the breakpoint return pointer, a state variable is needed to keep + track of whether it is a static (compiled) or dynamic (gdb-invoked) + breakpoint to be handled. A static breakpoint uses the content of register + BRP as it is whereas a dynamic breakpoint requires subtraction with 2 + in order to execute the instruction. The first breakpoint is static. */ +static unsigned char is_dyn_brkp = 0; + +/********************************* String library ****************************/ +/* Single-step over library functions creates trap loops. */ + +/* Copy char s2[] to s1[]. */ +static char* +gdb_cris_strcpy (char *s1, const char *s2) +{ + char *s = s1; + + for (s = s1; (*s++ = *s2++) != '\0'; ) + ; + return (s1); +} + +/* Find length of s[]. */ +static int +gdb_cris_strlen (const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; sc++) + ; + return (sc - s); +} + +/* Find first occurrence of c in s[n]. */ +static void* +gdb_cris_memchr (const void *s, int c, int n) +{ + const unsigned char uc = c; + const unsigned char *su; + + for (su = s; 0 < n; ++su, --n) + if (*su == uc) + return ((void *)su); + return (NULL); +} +/******************************* Standard library ****************************/ +/* Single-step over library functions creates trap loops. */ +/* Convert string to long. */ +static int +gdb_cris_strtol (const char *s, char **endptr, int base) +{ + char *s1; + char *sd; + int x = 0; + + for (s1 = (char*)s; (sd = gdb_cris_memchr(hexchars, *s1, base)) != NULL; ++s1) + x = x * base + (sd - hexchars); + + if (endptr) + { + /* Unconverted suffix is stored in endptr unless endptr is NULL. */ + *endptr = s1; + } + + return x; +} + +int +double_this(int x) +{ + return 2 * x; +} + +/********************************* Register image ****************************/ +/* Copy the content of a register image into another. The size n is + the size of the register image. Due to struct assignment generation of + memcpy in libc. */ +static void +copy_registers (registers *dptr, registers *sptr, int n) +{ + unsigned char *dreg; + unsigned char *sreg; + + for (dreg = (unsigned char*)dptr, sreg = (unsigned char*)sptr; n > 0; n--) + *dreg++ = *sreg++; +} + +#ifdef PROCESS_SUPPORT +/* Copy the stored registers from the stack. Put the register contents + of thread thread_id in the struct reg. */ +static void +copy_registers_from_stack (int thread_id, registers *regptr) +{ + int j; + stack_registers *s = (stack_registers *)stack_list[thread_id]; + unsigned int *d = (unsigned int *)regptr; + + for (j = 13; j >= 0; j--) + *d++ = s->r[j]; + regptr->sp = (unsigned int)stack_list[thread_id]; + regptr->pc = s->pc; + regptr->dccr = s->dccr; + regptr->srp = s->srp; +} + +/* Copy the registers to the stack. Put the register contents of thread + thread_id from struct reg to the stack. */ +static void +copy_registers_to_stack (int thread_id, registers *regptr) +{ + int i; + stack_registers *d = (stack_registers *)stack_list[thread_id]; + unsigned int *s = (unsigned int *)regptr; + + for (i = 0; i < 14; i++) { + d->r[i] = *s++; + } + d->pc = regptr->pc; + d->dccr = regptr->dccr; + d->srp = regptr->srp; +} +#endif + +/* Write a value to a specified register in the register image of the current + thread. Returns status code SUCCESS, E02 or E05. */ +static int +write_register (int regno, char *val) +{ + int status = SUCCESS; + registers *current_reg = ® + + if (regno >= R0 && regno <= PC) { + /* 32-bit register with simple offset. */ + hex2mem ((unsigned char *)current_reg + regno * sizeof(unsigned int), + val, sizeof(unsigned int)); + } + else if (regno == P0 || regno == VR || regno == P4 || regno == P8) { + /* Do not support read-only registers. */ + status = E02; + } + else if (regno == CCR) { + /* 16 bit register with complex offset. (P4 is read-only, P6 is not implemented, + and P7 (MOF) is 32 bits in ETRAX 100LX. */ + hex2mem ((unsigned char *)&(current_reg->ccr) + (regno-CCR) * sizeof(unsigned short), + val, sizeof(unsigned short)); + } + else if (regno >= MOF && regno <= USP) { + /* 32 bit register with complex offset. (P8 has been taken care of.) */ + hex2mem ((unsigned char *)&(current_reg->ibr) + (regno-IBR) * sizeof(unsigned int), + val, sizeof(unsigned int)); + } + else { + /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */ + status = E05; + } + return status; +} + +#ifdef PROCESS_SUPPORT +/* Write a value to a specified register in the stack of a thread other + than the current thread. Returns status code SUCCESS or E07. */ +static int +write_stack_register (int thread_id, int regno, char *valptr) +{ + int status = SUCCESS; + stack_registers *d = (stack_registers *)stack_list[thread_id]; + unsigned int val; + + hex2mem ((unsigned char *)&val, valptr, sizeof(unsigned int)); + if (regno >= R0 && regno < SP) { + d->r[regno] = val; + } + else if (regno == SP) { + stack_list[thread_id] = val; + } + else if (regno == PC) { + d->pc = val; + } + else if (regno == SRP) { + d->srp = val; + } + else if (regno == DCCR) { + d->dccr = val; + } + else { + /* Do not support registers in the current thread. */ + status = E07; + } + return status; +} +#endif + +/* Read a value from a specified register in the register image. Returns the + value in the register or -1 for non-implemented registers. + Should check consistency_status after a call which may be E05 after changes + in the implementation. */ +static int +read_register (char regno, unsigned int *valptr) +{ + registers *current_reg = ® + + if (regno >= R0 && regno <= PC) { + /* 32-bit register with simple offset. */ + *valptr = *(unsigned int *)((char *)current_reg + regno * sizeof(unsigned int)); + return SUCCESS; + } + else if (regno == P0 || regno == VR) { + /* 8 bit register with complex offset. */ + *valptr = (unsigned int)(*(unsigned char *) + ((char *)&(current_reg->p0) + (regno-P0) * sizeof(char))); + return SUCCESS; + } + else if (regno == P4 || regno == CCR) { + /* 16 bit register with complex offset. */ + *valptr = (unsigned int)(*(unsigned short *) + ((char *)&(current_reg->p4) + (regno-P4) * sizeof(unsigned short))); + return SUCCESS; + } + else if (regno >= MOF && regno <= USP) { + /* 32 bit register with complex offset. */ + *valptr = *(unsigned int *)((char *)&(current_reg->p8) + + (regno-P8) * sizeof(unsigned int)); + return SUCCESS; + } + else { + /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */ + consistency_status = E05; + return E05; + } +} + +/********************************** Packet I/O ******************************/ +/* Returns the character equivalent of a nibble, bit 7, 6, 5, and 4 of a byte, + represented by int x. */ +static inline char +highhex(int x) +{ + return hexchars[(x >> 4) & 0xf]; +} + +/* Returns the character equivalent of a nibble, bit 3, 2, 1, and 0 of a byte, + represented by int x. */ +static inline char +lowhex(int x) +{ + return hexchars[x & 0xf]; +} + +/* Returns the integer equivalent of a hexadecimal character. */ +static int +hex (char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* Convert the memory, pointed to by mem into hexadecimal representation. + Put the result in buf, and return a pointer to the last character + in buf (null). */ + +static int do_printk = 0; + +static char * +mem2hex(char *buf, unsigned char *mem, int count) +{ + int i; + int ch; + + if (mem == NULL) { + /* Bogus read from m0. FIXME: What constitutes a valid address? */ + for (i = 0; i < count; i++) { + *buf++ = '0'; + *buf++ = '0'; + } + } else { + /* Valid mem address. */ + for (i = 0; i < count; i++) { + ch = *mem++; + *buf++ = highhex (ch); + *buf++ = lowhex (ch); + } + } + + /* Terminate properly. */ + *buf = '\0'; + return (buf); +} + +/* Convert the array, in hexadecimal representation, pointed to by buf into + binary representation. Put the result in mem, and return a pointer to + the character after the last byte written. */ +static unsigned char* +hex2mem (unsigned char *mem, char *buf, int count) +{ + int i; + unsigned char ch; + for (i = 0; i < count; i++) { + ch = hex (*buf++) << 4; + ch = ch + hex (*buf++); + *mem++ = ch; + } + return (mem); +} + +/* Put the content of the array, in binary representation, pointed to by buf + into memory pointed to by mem, and return a pointer to the character after + the last byte written. + Gdb will escape $, #, and the escape char (0x7d). */ +static unsigned char* +bin2mem (unsigned char *mem, unsigned char *buf, int count) +{ + int i; + unsigned char *next; + for (i = 0; i < count; i++) { + /* Check for any escaped characters. Be paranoid and + only unescape chars that should be escaped. */ + if (*buf == 0x7d) { + next = buf + 1; + if (*next == 0x3 || *next == 0x4 || *next == 0x5D) /* #, $, ESC */ + { + buf++; + *buf += 0x20; + } + } + *mem++ = *buf++; + } + return (mem); +} + +/* Await the sequence $<data>#<checksum> and store <data> in the array buffer + returned. */ +static void +getpacket (char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + do { + while ((ch = getDebugChar ()) != '$') + /* Wait for the start character $ and ignore all other characters */; + checksum = 0; + xmitcsum = -1; + count = 0; + /* Read until a # or the end of the buffer is reached */ + while (count < BUFMAX) { + ch = getDebugChar (); + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = '\0'; + + if (ch == '#') { + xmitcsum = hex (getDebugChar ()) << 4; + xmitcsum += hex (getDebugChar ()); + if (checksum != xmitcsum) { + /* Wrong checksum */ + putDebugChar ('-'); + } + else { + /* Correct checksum */ + putDebugChar ('+'); + /* If sequence characters are received, reply with them */ + if (buffer[2] == ':') { + putDebugChar (buffer[0]); + putDebugChar (buffer[1]); + /* Remove the sequence characters from the buffer */ + count = gdb_cris_strlen (buffer); + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* Send $<data>#<checksum> from the <data> in the array buffer. */ + +static void +putpacket(char *buffer) +{ + int checksum; + int runlen; + int encode; + + do { + char *src = buffer; + putDebugChar ('$'); + checksum = 0; + while (*src) { + /* Do run length encoding */ + putDebugChar (*src); + checksum += *src; + runlen = 0; + while (runlen < RUNLENMAX && *src == src[runlen]) { + runlen++; + } + if (runlen > 3) { + /* Got a useful amount */ + putDebugChar ('*'); + checksum += '*'; + encode = runlen + ' ' - 4; + putDebugChar (encode); + checksum += encode; + src += runlen; + } + else { + src++; + } + } + putDebugChar ('#'); + putDebugChar (highhex (checksum)); + putDebugChar (lowhex (checksum)); + } while(kgdb_started && (getDebugChar() != '+')); +} + +/* The string str is prepended with the GDB printout token and sent. Required + in traditional implementations. */ +void +putDebugString (const unsigned char *str, int length) +{ + remcomOutBuffer[0] = 'O'; + mem2hex(&remcomOutBuffer[1], (unsigned char *)str, length); + putpacket(remcomOutBuffer); +} + +/********************************** Handle exceptions ************************/ +/* Build and send a response packet in order to inform the host the + stub is stopped. TAAn...:r...;n...:r...;n...:r...; + AA = signal number + n... = register number (hex) + r... = register contents + n... = `thread' + r... = thread process ID. This is a hex integer. + n... = other string not starting with valid hex digit. + gdb should ignore this n,r pair and go on to the next. + This way we can extend the protocol. */ +static void +stub_is_stopped(int sigval) +{ + char *ptr = remcomOutBuffer; + int regno; + + unsigned int reg_cont; + int status; + + /* Send trap type (converted to signal) */ + + *ptr++ = 'T'; + *ptr++ = highhex (sigval); + *ptr++ = lowhex (sigval); + + /* Send register contents. We probably only need to send the + * PC, frame pointer and stack pointer here. Other registers will be + * explicitely asked for. But for now, send all. + */ + + for (regno = R0; regno <= USP; regno++) { + /* Store n...:r...; for the registers in the buffer. */ + + status = read_register (regno, ®_cont); + + if (status == SUCCESS) { + + *ptr++ = highhex (regno); + *ptr++ = lowhex (regno); + *ptr++ = ':'; + + ptr = mem2hex(ptr, (unsigned char *)®_cont, + register_size[regno]); + *ptr++ = ';'; + } + + } + +#ifdef PROCESS_SUPPORT + /* Store the registers of the executing thread. Assume that both step, + continue, and register content requests are with respect to this + thread. The executing task is from the operating system scheduler. */ + + current_thread_c = executing_task; + current_thread_g = executing_task; + + /* A struct assignment translates into a libc memcpy call. Avoid + all libc functions in order to prevent recursive break points. */ + copy_registers (®_g, ®, sizeof(registers)); + + /* Store thread:r...; with the executing task TID. */ + gdb_cris_strcpy (&remcomOutBuffer[pos], "thread:"); + pos += gdb_cris_strlen ("thread:"); + remcomOutBuffer[pos++] = highhex (executing_task); + remcomOutBuffer[pos++] = lowhex (executing_task); + gdb_cris_strcpy (&remcomOutBuffer[pos], ";"); +#endif + + /* null-terminate and send it off */ + + *ptr = 0; + + putpacket (remcomOutBuffer); +} + +/* All expected commands are sent from remote.c. Send a response according + to the description in remote.c. */ +static void +handle_exception (int sigval) +{ + /* Avoid warning of not used. */ + + USEDFUN(handle_exception); + USEDVAR(internal_stack[0]); + + /* Send response. */ + + stub_is_stopped (sigval); + + for (;;) { + remcomOutBuffer[0] = '\0'; + getpacket (remcomInBuffer); + switch (remcomInBuffer[0]) { + case 'g': + /* Read registers: g + Success: Each byte of register data is described by two hex digits. + Registers are in the internal order for GDB, and the bytes + in a register are in the same order the machine uses. + Failure: void. */ + + { +#ifdef PROCESS_SUPPORT + /* Use the special register content in the executing thread. */ + copy_registers (®_g, ®, sizeof(registers)); + /* Replace the content available on the stack. */ + if (current_thread_g != executing_task) { + copy_registers_from_stack (current_thread_g, ®_g); + } + mem2hex ((unsigned char *)remcomOutBuffer, (unsigned char *)®_g, sizeof(registers)); +#else + mem2hex(remcomOutBuffer, (char *)®, sizeof(registers)); +#endif + } + break; + + case 'G': + /* Write registers. GXX..XX + Each byte of register data is described by two hex digits. + Success: OK + Failure: void. */ +#ifdef PROCESS_SUPPORT + hex2mem ((unsigned char *)®_g, &remcomInBuffer[1], sizeof(registers)); + if (current_thread_g == executing_task) { + copy_registers (®, ®_g, sizeof(registers)); + } + else { + copy_registers_to_stack(current_thread_g, ®_g); + } +#else + hex2mem((char *)®, &remcomInBuffer[1], sizeof(registers)); +#endif + gdb_cris_strcpy (remcomOutBuffer, "OK"); + break; + + case 'P': + /* Write register. Pn...=r... + Write register n..., hex value without 0x, with value r..., + which contains a hex value without 0x and two hex digits + for each byte in the register (target byte order). P1f=11223344 means + set register 31 to 44332211. + Success: OK + Failure: E02, E05 */ + { + char *suffix; + int regno = gdb_cris_strtol (&remcomInBuffer[1], &suffix, 16); + int status; +#ifdef PROCESS_SUPPORT + if (current_thread_g != executing_task) + status = write_stack_register (current_thread_g, regno, suffix+1); + else +#endif + status = write_register (regno, suffix+1); + + switch (status) { + case E02: + /* Do not support read-only registers. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E02]); + break; + case E05: + /* Do not support non-existing registers. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E05]); + break; + case E07: + /* Do not support non-existing registers on the stack. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E07]); + break; + default: + /* Valid register number. */ + gdb_cris_strcpy (remcomOutBuffer, "OK"); + break; + } + } + break; + + case 'm': + /* Read from memory. mAA..AA,LLLL + AA..AA is the address and LLLL is the length. + Success: XX..XX is the memory content. Can be fewer bytes than + requested if only part of the data may be read. m6000120a,6c means + retrieve 108 byte from base address 6000120a. + Failure: void. */ + { + char *suffix; + unsigned char *addr = (unsigned char *)gdb_cris_strtol(&remcomInBuffer[1], + &suffix, 16); int length = gdb_cris_strtol(suffix+1, 0, 16); + + mem2hex(remcomOutBuffer, addr, length); + } + break; + + case 'X': + /* Write to memory. XAA..AA,LLLL:XX..XX + AA..AA is the start address, LLLL is the number of bytes, and + XX..XX is the binary data. + Success: OK + Failure: void. */ + case 'M': + /* Write to memory. MAA..AA,LLLL:XX..XX + AA..AA is the start address, LLLL is the number of bytes, and + XX..XX is the hexadecimal data. + Success: OK + Failure: void. */ + { + char *lenptr; + char *dataptr; + unsigned char *addr = (unsigned char *)gdb_cris_strtol(&remcomInBuffer[1], + &lenptr, 16); + int length = gdb_cris_strtol(lenptr+1, &dataptr, 16); + if (*lenptr == ',' && *dataptr == ':') { + if (remcomInBuffer[0] == 'M') { + hex2mem(addr, dataptr + 1, length); + } + else /* X */ { + bin2mem(addr, dataptr + 1, length); + } + gdb_cris_strcpy (remcomOutBuffer, "OK"); + } + else { + gdb_cris_strcpy (remcomOutBuffer, error_message[E06]); + } + } + break; + + case 'c': + /* Continue execution. cAA..AA + AA..AA is the address where execution is resumed. If AA..AA is + omitted, resume at the present address. + Success: return to the executing thread. + Failure: will never know. */ + if (remcomInBuffer[1] != '\0') { + reg.pc = gdb_cris_strtol (&remcomInBuffer[1], 0, 16); + } + enableDebugIRQ(); + return; + + case 's': + /* Step. sAA..AA + AA..AA is the address where execution is resumed. If AA..AA is + omitted, resume at the present address. Success: return to the + executing thread. Failure: will never know. + + Should never be invoked. The single-step is implemented on + the host side. If ever invoked, it is an internal error E04. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E04]); + putpacket (remcomOutBuffer); + return; + + case '?': + /* The last signal which caused a stop. ? + Success: SAA, where AA is the signal number. + Failure: void. */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = highhex (sigval); + remcomOutBuffer[2] = lowhex (sigval); + remcomOutBuffer[3] = 0; + break; + + case 'D': + /* Detach from host. D + Success: OK, and return to the executing thread. + Failure: will never know */ + putpacket ("OK"); + return; + + case 'k': + case 'r': + /* kill request or reset request. + Success: restart of target. + Failure: will never know. */ + kill_restart (); + break; + + case 'C': + case 'S': + case '!': + case 'R': + case 'd': + /* Continue with signal sig. Csig;AA..AA + Step with signal sig. Ssig;AA..AA + Use the extended remote protocol. ! + Restart the target system. R0 + Toggle debug flag. d + Search backwards. tAA:PP,MM + Not supported: E04 */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E04]); + break; +#ifdef PROCESS_SUPPORT + + case 'T': + /* Thread alive. TXX + Is thread XX alive? + Success: OK, thread XX is alive. + Failure: E03, thread XX is dead. */ + { + int thread_id = (int)gdb_cris_strtol (&remcomInBuffer[1], 0, 16); + /* Cannot tell whether it is alive or not. */ + if (thread_id >= 0 && thread_id < number_of_tasks) + gdb_cris_strcpy (remcomOutBuffer, "OK"); + } + break; + + case 'H': + /* Set thread for subsequent operations: Hct + c = 'c' for thread used in step and continue; + t can be -1 for all threads. + c = 'g' for thread used in other operations. + t = 0 means pick any thread. + Success: OK + Failure: E01 */ + { + int thread_id = gdb_cris_strtol (&remcomInBuffer[2], 0, 16); + if (remcomInBuffer[1] == 'c') { + /* c = 'c' for thread used in step and continue */ + /* Do not change current_thread_c here. It would create a mess in + the scheduler. */ + gdb_cris_strcpy (remcomOutBuffer, "OK"); + } + else if (remcomInBuffer[1] == 'g') { + /* c = 'g' for thread used in other operations. + t = 0 means pick any thread. Impossible since the scheduler does + not allow that. */ + if (thread_id >= 0 && thread_id < number_of_tasks) { + current_thread_g = thread_id; + gdb_cris_strcpy (remcomOutBuffer, "OK"); + } + else { + /* Not expected - send an error message. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E01]); + } + } + else { + /* Not expected - send an error message. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E01]); + } + } + break; + + case 'q': + case 'Q': + /* Query of general interest. qXXXX + Set general value XXXX. QXXXX=yyyy */ + { + int pos; + int nextpos; + int thread_id; + + switch (remcomInBuffer[1]) { + case 'C': + /* Identify the remote current thread. */ + gdb_cris_strcpy (&remcomOutBuffer[0], "QC"); + remcomOutBuffer[2] = highhex (current_thread_c); + remcomOutBuffer[3] = lowhex (current_thread_c); + remcomOutBuffer[4] = '\0'; + break; + case 'L': + gdb_cris_strcpy (&remcomOutBuffer[0], "QM"); + /* Reply with number of threads. */ + if (os_is_started()) { + remcomOutBuffer[2] = highhex (number_of_tasks); + remcomOutBuffer[3] = lowhex (number_of_tasks); + } + else { + remcomOutBuffer[2] = highhex (0); + remcomOutBuffer[3] = lowhex (1); + } + /* Done with the reply. */ + remcomOutBuffer[4] = lowhex (1); + pos = 5; + /* Expects the argument thread id. */ + for (; pos < (5 + HEXCHARS_IN_THREAD_ID); pos++) + remcomOutBuffer[pos] = remcomInBuffer[pos]; + /* Reply with the thread identifiers. */ + if (os_is_started()) { + /* Store the thread identifiers of all tasks. */ + for (thread_id = 0; thread_id < number_of_tasks; thread_id++) { + nextpos = pos + HEXCHARS_IN_THREAD_ID - 1; + for (; pos < nextpos; pos ++) + remcomOutBuffer[pos] = lowhex (0); + remcomOutBuffer[pos++] = lowhex (thread_id); + } + } + else { + /* Store the thread identifier of the boot task. */ + nextpos = pos + HEXCHARS_IN_THREAD_ID - 1; + for (; pos < nextpos; pos ++) + remcomOutBuffer[pos] = lowhex (0); + remcomOutBuffer[pos++] = lowhex (current_thread_c); + } + remcomOutBuffer[pos] = '\0'; + break; + default: + /* Not supported: "" */ + /* Request information about section offsets: qOffsets. */ + remcomOutBuffer[0] = 0; + break; + } + } + break; +#endif /* PROCESS_SUPPORT */ + + default: + /* The stub should ignore other request and send an empty + response ($#<checksum>). This way we can extend the protocol and GDB + can tell whether the stub it is talking to uses the old or the new. */ + remcomOutBuffer[0] = 0; + break; + } + putpacket(remcomOutBuffer); + } +} + +/* The jump is to the address 0x00000002. Performs a complete re-start + from scratch. */ +static void +kill_restart () +{ + __asm__ volatile ("jump 2"); +} + +/********************************** Breakpoint *******************************/ +/* The hook for both a static (compiled) and a dynamic breakpoint set by GDB. + An internal stack is used by the stub. The register image of the caller is + stored in the structure register_image. + Interactive communication with the host is handled by handle_exception and + finally the register image is restored. */ + +void kgdb_handle_breakpoint(void); + +asm (" + .global kgdb_handle_breakpoint +kgdb_handle_breakpoint: +;; +;; Response to the break-instruction +;; +;; Create a register image of the caller +;; + move $dccr,[reg+0x5E] ; Save the flags in DCCR before disable interrupts + di ; Disable interrupts + move.d $r0,[reg] ; Save R0 + move.d $r1,[reg+0x04] ; Save R1 + move.d $r2,[reg+0x08] ; Save R2 + move.d $r3,[reg+0x0C] ; Save R3 + move.d $r4,[reg+0x10] ; Save R4 + move.d $r5,[reg+0x14] ; Save R5 + move.d $r6,[reg+0x18] ; Save R6 + move.d $r7,[reg+0x1C] ; Save R7 + move.d $r8,[reg+0x20] ; Save R8 + move.d $r9,[reg+0x24] ; Save R9 + move.d $r10,[reg+0x28] ; Save R10 + move.d $r11,[reg+0x2C] ; Save R11 + move.d $r12,[reg+0x30] ; Save R12 + move.d $r13,[reg+0x34] ; Save R13 + move.d $sp,[reg+0x38] ; Save SP (R14) +;; Due to the old assembler-versions BRP might not be recognized + .word 0xE670 ; move brp,$r0 + subq 2,$r0 ; Set to address of previous instruction. + move.d $r0,[reg+0x3c] ; Save the address in PC (R15) + clear.b [reg+0x40] ; Clear P0 + move $vr,[reg+0x41] ; Save special register P1 + clear.w [reg+0x42] ; Clear P4 + move $ccr,[reg+0x44] ; Save special register CCR + move $mof,[reg+0x46] ; P7 + clear.d [reg+0x4A] ; Clear P8 + move $ibr,[reg+0x4E] ; P9, + move $irp,[reg+0x52] ; P10, + move $srp,[reg+0x56] ; P11, + move $dtp0,[reg+0x5A] ; P12, register BAR, assembler might not know BAR + ; P13, register DCCR already saved +;; Due to the old assembler-versions BRP might not be recognized + .word 0xE670 ; move brp,r0 +;; Static (compiled) breakpoints must return to the next instruction in order +;; to avoid infinite loops. Dynamic (gdb-invoked) must restore the instruction +;; in order to execute it when execution is continued. + test.b [is_dyn_brkp] ; Is this a dynamic breakpoint? + beq is_static ; No, a static breakpoint + nop + subq 2,$r0 ; rerun the instruction the break replaced +is_static: + moveq 1,$r1 + move.b $r1,[is_dyn_brkp] ; Set the state variable to dynamic breakpoint + move.d $r0,[reg+0x62] ; Save the return address in BRP + move $usp,[reg+0x66] ; USP +;; +;; Handle the communication +;; + move.d internal_stack+1020,$sp ; Use the internal stack which grows upward + moveq 5,$r10 ; SIGTRAP + jsr handle_exception ; Interactive routine +;; +;; Return to the caller +;; + move.d [reg],$r0 ; Restore R0 + move.d [reg+0x04],$r1 ; Restore R1 + move.d [reg+0x08],$r2 ; Restore R2 + move.d [reg+0x0C],$r3 ; Restore R3 + move.d [reg+0x10],$r4 ; Restore R4 + move.d [reg+0x14],$r5 ; Restore R5 + move.d [reg+0x18],$r6 ; Restore R6 + move.d [reg+0x1C],$r7 ; Restore R7 + move.d [reg+0x20],$r8 ; Restore R8 + move.d [reg+0x24],$r9 ; Restore R9 + move.d [reg+0x28],$r10 ; Restore R10 + move.d [reg+0x2C],$r11 ; Restore R11 + move.d [reg+0x30],$r12 ; Restore R12 + move.d [reg+0x34],$r13 ; Restore R13 +;; +;; FIXME: Which registers should be restored? +;; + move.d [reg+0x38],$sp ; Restore SP (R14) + move [reg+0x56],$srp ; Restore the subroutine return pointer. + move [reg+0x5E],$dccr ; Restore DCCR + move [reg+0x66],$usp ; Restore USP + jump [reg+0x62] ; A jump to the content in register BRP works. + nop ; +"); + +/* The hook for an interrupt generated by GDB. An internal stack is used + by the stub. The register image of the caller is stored in the structure + register_image. Interactive communication with the host is handled by + handle_exception and finally the register image is restored. Due to the + old assembler which does not recognise the break instruction and the + breakpoint return pointer hex-code is used. */ + +void kgdb_handle_serial(void); + +asm (" + .global kgdb_handle_serial +kgdb_handle_serial: +;; +;; Response to a serial interrupt +;; + + move $dccr,[reg+0x5E] ; Save the flags in DCCR + di ; Disable interrupts + move.d $r0,[reg] ; Save R0 + move.d $r1,[reg+0x04] ; Save R1 + move.d $r2,[reg+0x08] ; Save R2 + move.d $r3,[reg+0x0C] ; Save R3 + move.d $r4,[reg+0x10] ; Save R4 + move.d $r5,[reg+0x14] ; Save R5 + move.d $r6,[reg+0x18] ; Save R6 + move.d $r7,[reg+0x1C] ; Save R7 + move.d $r8,[reg+0x20] ; Save R8 + move.d $r9,[reg+0x24] ; Save R9 + move.d $r10,[reg+0x28] ; Save R10 + move.d $r11,[reg+0x2C] ; Save R11 + move.d $r12,[reg+0x30] ; Save R12 + move.d $r13,[reg+0x34] ; Save R13 + move.d $sp,[reg+0x38] ; Save SP (R14) + move $irp,[reg+0x3c] ; Save the address in PC (R15) + clear.b [reg+0x40] ; Clear P0 + move $vr,[reg+0x41] ; Save special register P1, + clear.w [reg+0x42] ; Clear P4 + move $ccr,[reg+0x44] ; Save special register CCR + move $mof,[reg+0x46] ; P7 + clear.d [reg+0x4A] ; Clear P8 + move $ibr,[reg+0x4E] ; P9, + move $irp,[reg+0x52] ; P10, + move $srp,[reg+0x56] ; P11, + move $dtp0,[reg+0x5A] ; P12, register BAR, assembler might not know BAR + ; P13, register DCCR already saved +;; Due to the old assembler-versions BRP might not be recognized + .word 0xE670 ; move brp,r0 + move.d $r0,[reg+0x62] ; Save the return address in BRP + move $usp,[reg+0x66] ; USP + +;; get the serial character (from debugport.c) and check if it is a ctrl-c + + jsr getDebugChar + cmp.b 3, $r10 + bne goback + nop + +;; +;; Handle the communication +;; + move.d internal_stack+1020,$sp ; Use the internal stack + moveq 2,$r10 ; SIGINT + jsr handle_exception ; Interactive routine + +goback: +;; +;; Return to the caller +;; + move.d [reg],$r0 ; Restore R0 + move.d [reg+0x04],$r1 ; Restore R1 + move.d [reg+0x08],$r2 ; Restore R2 + move.d [reg+0x0C],$r3 ; Restore R3 + move.d [reg+0x10],$r4 ; Restore R4 + move.d [reg+0x14],$r5 ; Restore R5 + move.d [reg+0x18],$r6 ; Restore R6 + move.d [reg+0x1C],$r7 ; Restore R7 + move.d [reg+0x20],$r8 ; Restore R8 + move.d [reg+0x24],$r9 ; Restore R9 + move.d [reg+0x28],$r10 ; Restore R10 + move.d [reg+0x2C],$r11 ; Restore R11 + move.d [reg+0x30],$r12 ; Restore R12 + move.d [reg+0x34],$r13 ; Restore R13 +;; +;; FIXME: Which registers should be restored? +;; + move.d [reg+0x38],$sp ; Restore SP (R14) + move [reg+0x56],$srp ; Restore the subroutine return pointer. + move [reg+0x5E],$dccr ; Restore DCCR + move [reg+0x66],$usp ; Restore USP + reti ; Return from the interrupt routine + nop +"); + +/* Use this static breakpoint in the start-up only. */ + +void +breakpoint(void) +{ + kgdb_started = 1; + is_dyn_brkp = 0; /* This is a static, not a dynamic breakpoint. */ + __asm__ volatile ("break 8"); /* Jump to handle_breakpoint. */ +} + +/* initialize kgdb. doesn't break into the debugger, but sets up irq and ports */ + +void +kgdb_init(void) +{ + /* could initialize debug port as well but it's done in head.S already... */ + + /* breakpoint handler is now set in irq.c */ + set_int_vector(8, kgdb_handle_serial); + + enableDebugIRQ(); +} + +/****************************** End of file **********************************/ diff --git a/arch/cris/arch-v10/kernel/process.c b/arch/cris/arch-v10/kernel/process.c new file mode 100644 index 00000000000..87ff3779082 --- /dev/null +++ b/arch/cris/arch-v10/kernel/process.c @@ -0,0 +1,270 @@ +/* $Id: process.c,v 1.9 2004/10/19 13:07:37 starvik Exp $ + * + * 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/config.h> +#include <linux/sched.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <asm/arch/svinto.h> +#include <linux/init.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 reseting 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 (unsigned long)user_regs(t->thread_info)->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(®s, 0, sizeof(regs)); + + /* Don't use r10 since that is set to 0 in copy_thread */ + regs.r11 = (unsigned long)fn; + regs.r12 = (unsigned long)arg; + regs.irp = (unsigned long)kernel_thread_helper; + + /* Ok, create the new process.. */ + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); +} + +/* setup the child's kernel stack with a pt_regs and 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(int nr, unsigned long clone_flags, unsigned long usp, + unsigned long unused, + struct task_struct *p, struct pt_regs *regs) +{ + struct pt_regs * childregs; + struct switch_stack *swstack; + + /* put the pt_regs structure at the end of the new kernel stack page and fix it up + * remember that the task_struct doubles as the kernel stack for the task + */ + + childregs = user_regs(p->thread_info); + + *childregs = *regs; /* struct copy of pt_regs */ + + p->set_child_tid = p->clear_child_tid = NULL; + + childregs->r10 = 0; /* child returns 0 after a fork/clone */ + + /* 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, char **argv, char **envp, + long r13, long mof, long srp, + struct pt_regs *regs) +{ + int error; + char *filename; + + filename = getname(fname); + error = PTR_ERR(filename); + + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, argv, envp, regs); + putname(filename); + out: + return error; +} + +unsigned long get_wchan(struct task_struct *p) +{ +#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); +} + diff --git a/arch/cris/arch-v10/kernel/ptrace.c b/arch/cris/arch-v10/kernel/ptrace.c new file mode 100644 index 00000000000..da15db8ae48 --- /dev/null +++ b/arch/cris/arch-v10/kernel/ptrace.c @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2000-2003, Axis Communications AB. + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> + +#include <asm/uaccess.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/system.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 *)user_regs(task->thread_info))[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 *)user_regs(task->thread_info))[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? */ +} + +/* + * 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). + */ +asmlinkage int +sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret; + unsigned long __user *datap = (unsigned long __user *)data; + + lock_kernel(); + ret = -EPERM; + + if (request == PTRACE_TRACEME) { + if (current->ptrace & PT_PTRACED) + goto out; + + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + + if (child) + get_task_struct(child); + + read_unlock(&tasklist_lock); + + if (!child) + goto out; + + ret = -EPERM; + + if (pid == 1) /* Leave the init process alone! */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) + goto out_tsk; + + switch (request) { + /* Read word at location address. */ + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + + if (copied != sizeof(tmp)) + break; + + ret = put_user(tmp,datap); + break; + } + + /* Read the word at location address in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || addr < 0 || addr > PT_MAX << 2) + break; + + tmp = get_reg(child, addr >> 2); + ret = put_user(tmp, datap); + break; + } + + /* Write the word at location address. */ + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + ret = 0; + + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + break; + + ret = -EIO; + break; + + /* Write the word at location address in the USER area. */ + case PTRACE_POKEUSR: + ret = -EIO; + if ((addr & 3) || addr < 0 || addr > PT_MAX << 2) + break; + + addr >>= 2; + + if (addr == PT_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, addr, data)) + break; + ret = 0; + break; + + case PTRACE_SYSCALL: + case PTRACE_CONT: + ret = -EIO; + + if ((unsigned long) data > _NSIG) + break; + + if (request == PTRACE_SYSCALL) { + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + else { + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + + child->exit_code = data; + + /* TODO: make sure any pending breakpoint is killed */ + wake_up_process(child); + ret = 0; + + break; + + /* Make the child exit by sending it a sigkill. */ + case PTRACE_KILL: + ret = 0; + + if (child->state == TASK_ZOMBIE) + break; + + child->exit_code = SIGKILL; + + /* TODO: make sure any pending breakpoint is killed */ + wake_up_process(child); + break; + + /* Set the trap flag. */ + case PTRACE_SINGLESTEP: + ret = -EIO; + + if ((unsigned long) data > _NSIG) + break; + + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + + /* TODO: set some clever breakpoint mechanism... */ + + child->exit_code = data; + wake_up_process(child); + ret = 0; + break; + + case PTRACE_DETACH: + ret = ptrace_detach(child, data); + break; + + /* Get all GP registers from the child. */ + case PTRACE_GETREGS: { + int i; + unsigned long tmp; + + for (i = 0; i <= PT_MAX; i++) { + tmp = get_reg(child, i); + + if (put_user(tmp, datap)) { + ret = -EFAULT; + goto out_tsk; + } + + data += sizeof(long); + } + + ret = 0; + break; + } + + /* Set all GP registers in the child. */ + case PTRACE_SETREGS: { + int i; + unsigned long tmp; + + for (i = 0; i <= PT_MAX; i++) { + if (get_user(tmp, datap)) { + ret = -EFAULT; + goto out_tsk; + } + + if (i == PT_DCCR) { + tmp &= DCCR_MASK; + tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK; + } + + put_reg(child, i, tmp); + data += sizeof(long); + } + + ret = 0; + break; + } + + default: + ret = ptrace_request(child, request, addr, data); + break; + } +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +void do_syscall_trace(void) +{ + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + + if (!(current->ptrace & PT_PTRACED)) + return; + + /* the 0x80 provides a way for the tracing parent to distinguish + between a syscall stop and SIGTRAP delivery */ + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); + + /* + * This isn't the same as continuing with a signal, but it will do for + * normal use. + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff --git a/arch/cris/arch-v10/kernel/setup.c b/arch/cris/arch-v10/kernel/setup.c new file mode 100644 index 00000000000..b668d7fb68e --- /dev/null +++ b/arch/cris/arch-v10/kernel/setup.c @@ -0,0 +1,103 @@ +/* + * + * 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/config.h> +#include <linux/seq_file.h> +#include <linux/proc_fs.h> +#include <linux/delay.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 >= sizeof cpu_info/sizeof *cpu_info) + info = &cpu_info[sizeof cpu_info/sizeof *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"); +} diff --git a/arch/cris/arch-v10/kernel/shadows.c b/arch/cris/arch-v10/kernel/shadows.c new file mode 100644 index 00000000000..561a890a8e4 --- /dev/null +++ b/arch/cris/arch-v10/kernel/shadows.c @@ -0,0 +1,36 @@ +/* $Id: shadows.c,v 1.1 2001/12/17 13:59:27 bjornw Exp $ + * + * Various shadow registers. Defines for these are in include/asm-etrax100/io.h + */ + +/* Shadows for internal Etrax-registers */ + +unsigned long genconfig_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 LED's 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; + diff --git a/arch/cris/arch-v10/kernel/signal.c b/arch/cris/arch-v10/kernel/signal.c new file mode 100644 index 00000000000..85e0032e664 --- /dev/null +++ b/arch/cris/arch-v10/kernel/signal.c @@ -0,0 +1,580 @@ +/* + * 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, 2001 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.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> + +#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; + +int do_signal(int canrestart, sigset_t *oldset, 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) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->r10 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(0, &saveset, regs)) + /* We will get here twice: once to call the signal + handler, then again to return from the + sigsuspend system call. When calling the + signal handler, R10 holds the signal number as + set through do_signal. The sigsuspend call + will return with the restored value set above; + always -EINTR. */ + return regs->r10; + } +} + +/* Define dummy arguments to be able to reach the regs argument. (Note that + * this arrangement relies on size_t occupying one register.) + */ +int +sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13, + long mof, long srp, struct pt_regs *regs) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->r10 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(0, &saveset, regs)) + /* We will get here twice: once to call the signal + handler, then again to return from the + sigsuspend system call. When calling the + signal handler, R10 holds the signal number as + set through do_signal. The sigsuspend call + will return with the restored value set above; + always -EINTR. */ + return regs->r10; + } +} + +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(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->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(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) + goto badframe; + + if (do_sigaltstack(&frame->uc.uc_stack, NULL, rdusp()) == -EFAULT) + goto badframe; + + 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 void 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; + +give_sigsegv: + force_sigsegv(sig, current); +} + +static void 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 */ + + 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 */ + regs->r11 = (unsigned long) &frame->info; /* second argument is (siginfo_t *) */ + regs->r12 = 0; /* third argument is unused */ + + /* actually move the usp to reflect the stacked frame */ + + wrusp((unsigned long)frame); + + return; + +give_sigsegv: + force_sigsegv(sig, current); +} + +/* + * OK, we're invoking a handler + */ + +extern inline void +handle_signal(int canrestart, unsigned long sig, + siginfo_t *info, struct k_sigaction *ka, + sigset_t *oldset, struct pt_regs * regs) +{ + /* 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) + setup_rt_frame(sig, ka, info, oldset, regs); + else + setup_frame(sig, ka, oldset, regs); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Also note that the regs structure given here as an argument, is the latest + * pushed pt_regs. It may or may not be the same as the first pushed registers + * when the initial usermode->kernelmode transition took place. Therefore + * we can use user_mode(regs) to see if we came directly from kernel or user + * mode below. + */ + +int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + int signr; + struct k_sigaction ka; + + /* + * 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 1; + + if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { + /* Whee! Actually deliver the signal. */ + handle_signal(canrestart, signr, &info, &ka, oldset, regs); + return 1; + } + + /* 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->r10 = __NR_restart_syscall; + regs->irp -= 2; + } + } + return 0; +} diff --git a/arch/cris/arch-v10/kernel/time.c b/arch/cris/arch-v10/kernel/time.c new file mode 100644 index 00000000000..6b7b4e0802e --- /dev/null +++ b/arch/cris/arch-v10/kernel/time.c @@ -0,0 +1,369 @@ +/* $Id: time.c,v 1.5 2004/09/29 06:12:46 starvik Exp $ + * + * linux/arch/cris/arch-v10/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * Copyright (C) 1999-2002 Axis Communications AB + * + */ + +#include <linux/config.h> +#include <linux/timex.h> +#include <linux/time.h> +#include <linux/jiffies.h> +#include <linux/interrupt.h> +#include <linux/swap.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <asm/arch/svinto.h> +#include <asm/types.h> +#include <asm/signal.h> +#include <asm/io.h> +#include <asm/delay.h> +#include <asm/rtc.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 void update_xtime_from_cmos(void); +extern int set_rtc_mmss(unsigned long nowtime); +extern int setup_irq(int, struct irqaction *); +extern int have_rtc; + +unsigned long get_ns_in_jiffie(void) +{ + unsigned char timer_count, t1; + unsigned short presc_count; + unsigned long ns; + unsigned long flags; + + local_irq_save(flags); + local_irq_disable(); + 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, t1; + unsigned long usec_count = 0; + unsigned short presc_count; + + static unsigned long count_p = TIMER0_DIV;/* for the first call after boot */ + static unsigned long jiffies_p = 0; + + /* + * cache volatile jiffies temporarily; we have IRQs turned off. + */ + unsigned long jiffies_t; + + /* The timer interrupt comes from Etrax timer 0. In order to get + * better precision, we check the current value. It might have + * underflowed already though. + */ + +#ifndef CONFIG_SVINTO_SIM + /* Not available in the xsim simulator. */ + count = *R_TIMER0_DATA; + presc_count = *R_TIM_PRESC_STATUS; + /* presc_count might be wrapped */ + t1 = *R_TIMER0_DATA; + if (count != t1){ + /* it wrapped, read prescaler again... */ + presc_count = *R_TIM_PRESC_STATUS; + count = t1; + } +#else + count = 0; + presc_count = 0; +#endif + + jiffies_t = jiffies; + + /* + * avoiding timer inconsistencies (they are rare, but they happen)... + * there are one problem that must be avoided here: + * 1. the timer counter underflows + */ + if( jiffies_t == jiffies_p ) { + if( count > count_p ) { + /* Timer wrapped, use new count and prescale + * increase the time corresponding to one jiffie + */ + usec_count = 1000000/HZ; + } + } else + jiffies_p = jiffies_t; + count_p = count; + 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; + } + /* Convert timer value to usec */ + usec_count += ( (TIMER0_DIV - count) * (1000000/HZ)/TIMER0_DIV ) + + (( (presc_count) * (1000000000/PRESCALE_FREQ))/1000); + + return usec_count; +} + +/* 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 +} + +/* last time the cmos clock got updated */ +static long last_rtc_update = 0; + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ + +//static unsigned short myjiff; /* used by our debug routine print_timestamp */ + +extern void cris_do_profile(struct pt_regs *regs); + +static inline irqreturn_t +timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* 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(); + + /* call the real timer interrupt handler */ + + do_timer(regs); + + cris_do_profile(regs); /* Save profiling information */ + + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + * + * The division here is not time critical since it will run once in + * 11 minutes + */ + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + (xtime.tv_nsec / 1000) >= 500000 - (tick_nsec / 1000) / 2 && + (xtime.tv_nsec / 1000) <= 500000 + (tick_nsec / 1000) / 2) { + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + } + return IRQ_HANDLED; +} + +/* timer is SA_SHIRQ so drivers can add stuff to the timer irq chain + * it needs to be SA_INTERRUPT to make the jiffies update work properly + */ + +static struct irqaction irq2 = { timer_interrupt, SA_SHIRQ | SA_INTERRUPT, + CPU_MASK_NONE, "timer", NULL, NULL}; + +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) { + /* no RTC, start at 1980 */ + xtime.tv_sec = 0; + xtime.tv_nsec = 0; + have_rtc = 0; + } else { + /* get the current time */ + have_rtc = 1; + update_xtime_from_cmos(); + } + + /* + * Initialize wall_to_monotonic such that adding it to xtime will yield zero, the + * tv_nsec field must be normalized (i.e., 0 <= nsec < NSEC_PER_SEC). + */ + set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec); + + /* 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 +} diff --git a/arch/cris/arch-v10/kernel/traps.c b/arch/cris/arch-v10/kernel/traps.c new file mode 100644 index 00000000000..da491f438a6 --- /dev/null +++ b/arch/cris/arch-v10/kernel/traps.c @@ -0,0 +1,132 @@ +/* $Id: traps.c,v 1.2 2003/07/04 08:27:41 starvik Exp $ + * + * linux/arch/cris/arch-v10/traps.c + * + * Heler functions for trap handlers + * + * Copyright (C) 2000-2002 Axis Communications AB + * + * Authors: Bjorn Wesen + * Hans-Peter Nilsson + * + */ + +#include <linux/config.h> +#include <linux/ptrace.h> +#include <asm/uaccess.h> +#include <asm/arch/sv_addr_ag.h> + +void +show_registers(struct pt_regs * regs) +{ + /* We either use rdusp() - the USP register, which might not + correspond to the current process for all cases we're called, + or we use the current->thread.usp, which is not up to date for + the current process. Experience shows we want the USP + register. */ + 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); + 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); + + /* Dump kernel stack if the previous dump wasn't one. */ + if (usp != 0) + show_stack (NULL, NULL); + + printk("\nCode: "); + if(regs->irp < PAGE_OFFSET) + goto bad; + + /* Often enough the value at regs->irp does not point to + the interesting instruction, which is most often the + _previous_ instruction. So we dump at an offset large + enough that instruction decoding should be in sync at + the interesting point, but small enough to fit on a row + (sort of). We point out the regs->irp location in a + ksymoops-friendly way by wrapping the byte for that + address in parentheses. */ + for(i = -12; i < 12; i++) + { + unsigned char c; + if(__get_user(c, &((unsigned char*)regs->irp)[i])) { +bad: + printk(" Bad IP value."); + break; + } + + if (i == 0) + printk("(%02x) ", c); + else + printk("%02x ", c); + } + printk("\n"); + } +} + +/* Called from entry.S when the watchdog has bitten + * We print out something resembling an oops dump, and if + * we have the nice doggy development flag set, we halt here + * instead of rebooting. + */ + +extern void reset_watchdog(void); +extern void stop_watchdog(void); + + +void +watchdog_bite_hook(struct pt_regs *regs) +{ +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + local_irq_disable(); + stop_watchdog(); + show_registers(regs); + while(1) /* nothing */; +#else + show_registers(regs); +#endif +} + +/* This is normally the 'Oops' routine */ +void +die_if_kernel(const char * str, struct pt_regs * regs, long err) +{ + if(user_mode(regs)) + return; + +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + /* This printout might take too long and trigger the + * watchdog normally. If we're in the nice doggy + * development mode, stop the watchdog during printout. + */ + stop_watchdog(); +#endif + + printk("%s: %04lx\n", str, err & 0xffff); + + show_registers(regs); + +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + reset_watchdog(); +#endif + do_exit(SIGSEGV); +} diff --git a/arch/cris/arch-v10/lib/Makefile b/arch/cris/arch-v10/lib/Makefile new file mode 100644 index 00000000000..36e9a9c5239 --- /dev/null +++ b/arch/cris/arch-v10/lib/Makefile @@ -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 + diff --git a/arch/cris/arch-v10/lib/checksum.S b/arch/cris/arch-v10/lib/checksum.S new file mode 100644 index 00000000000..85c48f0a9ec --- /dev/null +++ b/arch/cris/arch-v10/lib/checksum.S @@ -0,0 +1,124 @@ +/* $Id: checksum.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ + * 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 + ax ; do it again, since we might have generated a carry + 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 + move.d $r12,$r13 ; do the same again, maybe we got a carry last add + lsrq 16,$r13 + and.d $r9,$r12 + add.d $r13,$r12 + +_no_fold: + cmpq 2,$r11 + blt _no_words + nop + + ;; checksum the rest of the words + + subq 2,$r11 + +_wloop: subq 2,$r11 + bge _wloop + addu.w [$r10+],$r12 + + addq 2,$r11 + +_no_words: + ;; see if we have one odd byte more + cmpq 1,$r11 + beq _do_byte + nop + ret + move.d $r12, $r10 + +_do_byte: + ;; copy and checksum the last byte + addu.b [$r10],$r12 + ret + move.d $r12, $r10 + diff --git a/arch/cris/arch-v10/lib/checksumcopy.S b/arch/cris/arch-v10/lib/checksumcopy.S new file mode 100644 index 00000000000..35cbffb306f --- /dev/null +++ b/arch/cris/arch-v10/lib/checksumcopy.S @@ -0,0 +1,132 @@ +/* $Id: checksumcopy.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ + * 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 + ax ; do it again, since we might have generated a carry + 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 + move.d $r13,$r9 ; do the same again, maybe we got a carry last add + lsrq 16,$r9 + and.d 0xffff,$r13 + add.d $r9,$r13 + +_no_fold: + cmpq 2,$r12 + blt _no_words + nop + + ;; copy and checksum the rest of the words + + subq 2,$r12 + +_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 diff --git a/arch/cris/arch-v10/lib/csumcpfruser.S b/arch/cris/arch-v10/lib/csumcpfruser.S new file mode 100644 index 00000000000..5f41ccd6275 --- /dev/null +++ b/arch/cris/arch-v10/lib/csumcpfruser.S @@ -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 diff --git a/arch/cris/arch-v10/lib/dmacopy.c b/arch/cris/arch-v10/lib/dmacopy.c new file mode 100644 index 00000000000..e5fb44f505c --- /dev/null +++ b/arch/cris/arch-v10/lib/dmacopy.c @@ -0,0 +1,43 @@ +/* $Id: dmacopy.c,v 1.1 2001/12/17 13:59:27 bjornw Exp $ + * + * 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("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("done\n")); + +} + + + diff --git a/arch/cris/arch-v10/lib/dram_init.S b/arch/cris/arch-v10/lib/dram_init.S new file mode 100644 index 00000000000..2ef4ad5706e --- /dev/null +++ b/arch/cris/arch-v10/lib/dram_init.S @@ -0,0 +1,205 @@ +/* $Id: dram_init.S,v 1.4 2003/09/22 09:21:59 starvik Exp $ + * + * DRAM/SDRAM initialization - alter with care + * This file is intended to be included from other assembler files + * + * Note: This file may not modify 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) + * + * $Log: dram_init.S,v $ + * Revision 1.4 2003/09/22 09:21:59 starvik + * Decompresser is linked to 0x407xxxxx and sdram commands are at 0x000xxxxx + * so we need to mask off 12 bits. + * + * Revision 1.3 2003/03/31 09:38:37 starvik + * Corrected calculation of end of sdram init commands + * + * Revision 1.2 2002/11/19 13:33:29 starvik + * Changes from Linux 2.4 + * + * Revision 1.13 2002/10/30 07:42:28 starvik + * Always read SDRAM command sequence from flash + * + * Revision 1.12 2002/08/09 11:37:37 orjanf + * Added double initialization work-around for Samsung SDRAMs. + * + * Revision 1.11 2002/06/04 11:43:21 starvik + * Check if mrs_data is specified in kernelconfig (necessary for MCM) + * + * Revision 1.10 2001/10/04 12:00:21 martinnn + * Added missing underscores. + * + * Revision 1.9 2001/10/01 14:47:35 bjornw + * Added register prefixes and removed underscores + * + * Revision 1.8 2001/05/15 07:12:45 hp + * Copy warning from head.S about r8 and r9 + * + * Revision 1.7 2001/04/18 12:05:39 bjornw + * Fixed comments, and explicitely include config.h to be sure its there + * + * Revision 1.6 2001/04/10 06:20:16 starvik + * Delay should be 200us, not 200ns + * + * Revision 1.5 2001/04/09 06:01:13 starvik + * Added support for 100 MHz SDRAMs + * + * Revision 1.4 2001/03/26 14:24:01 bjornw + * Namechange of some config options + * + * Revision 1.3 2001/03/23 08:29:41 starvik + * Corrected calculation of mrs_data + * + * Revision 1.2 2001/02/08 15:20:00 starvik + * Corrected SDRAM initialization + * Should now be included as inline + * + * Revision 1.1 2001/01/29 13:08:02 starvik + * Initial version + * This file should be included from all assembler files that needs to + * initialize DRAM/SDRAM. + * + */ + +/* Just to be certain the config file is included, we include it here + * explicitely instead of depending on it being included in the file that + * uses this code. + */ + +#include <linux/config.h> + + ;; 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 diff --git a/arch/cris/arch-v10/lib/hw_settings.S b/arch/cris/arch-v10/lib/hw_settings.S new file mode 100644 index 00000000000..56905aaa7b6 --- /dev/null +++ b/arch/cris/arch-v10/lib/hw_settings.S @@ -0,0 +1,62 @@ +/* + * $Id: hw_settings.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ + * + * This table is used by some tools to extract hardware parameters. + * The table should be included in the kernel and the decompressor. + * Don't forget to update the tools if you change this table. + * + * Copyright (C) 2001 Axis Communications AB + * + * Authors: Mikael Starvik (starvik@axis.com) + */ + +#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 diff --git a/arch/cris/arch-v10/lib/memset.c b/arch/cris/arch-v10/lib/memset.c new file mode 100644 index 00000000000..82bb6683917 --- /dev/null +++ b/arch/cris/arch-v10/lib/memset.c @@ -0,0 +1,252 @@ +/*#************************************************************************#*/ +/*#-------------------------------------------------------------------------*/ +/*# */ +/*# FUNCTION NAME: memset() */ +/*# */ +/*# PARAMETERS: void* dst; Destination address. */ +/*# int c; Value of byte to write. */ +/*# int len; Number of bytes to write. */ +/*# */ +/*# RETURNS: dst. */ +/*# */ +/*# DESCRIPTION: Sets the memory dst of length len bytes to c, as standard. */ +/*# Framework taken from memcpy. This routine is */ +/*# very sensitive to compiler changes in register allocation. */ +/*# Should really be rewritten to avoid this problem. */ +/*# */ +/*#-------------------------------------------------------------------------*/ +/*# */ +/*# HISTORY */ +/*# */ +/*# DATE NAME CHANGES */ +/*# ---- ---- ------- */ +/*# 990713 HP Tired of watching this function (or */ +/*# really, the nonoptimized generic */ +/*# implementation) take up 90% of simulator */ +/*# output. Measurements needed. */ +/*# */ +/*#-------------------------------------------------------------------------*/ + +#include <linux/types.h> + +/* No, there's no macro saying 12*4, since it is "hard" to get it into + the asm in a good way. Thus better to expose the problem everywhere. + */ + +/* Assuming 1 cycle per dword written or read (ok, not really true), and + one per instruction, then 43+3*(n/48-1) <= 24+24*(n/48-1) + so n >= 45.7; n >= 0.9; we win on the first full 48-byte block to set. */ + +#define ZERO_BLOCK_SIZE (1*12*4) + +void *memset(void *pdst, + int c, + size_t plen) +{ + /* Ok. Now we want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. */ + + register char *return_dst __asm__ ("r10") = pdst; + register int n __asm__ ("r12") = plen; + register int lc __asm__ ("r11") = c; + + /* Most apps use memset sanely. Only those memsetting about 3..4 + bytes or less get penalized compared to the generic implementation + - and that's not really sane use. */ + + /* Ugh. This is fragile at best. Check with newer GCC releases, if + they compile cascaded "x |= x << 8" sanely! */ + __asm__("movu.b %0,$r13\n\t" + "lslq 8,$r13\n\t" + "move.b %0,$r13\n\t" + "move.d $r13,%0\n\t" + "lslq 16,$r13\n\t" + "or.d $r13,%0" + : "=r" (lc) : "0" (lc) : "r13"); + + { + register char *dst __asm__ ("r13") = pdst; + + /* This is NONPORTABLE, but since this whole routine is */ + /* grossly nonportable that doesn't matter. */ + + if (((unsigned long) pdst & 3) != 0 + /* Oops! n=0 must be a legal call, regardless of alignment. */ + && n >= 3) + { + if ((unsigned long)dst & 1) + { + *dst = (char) lc; + n--; + dst++; + } + + if ((unsigned long)dst & 2) + { + *(short *)dst = lc; + n -= 2; + dst += 2; + } + } + + /* Now the fun part. For the threshold value of this, check the equation + above. */ + /* Decide which copying method to use. */ + if (n >= ZERO_BLOCK_SIZE) + { + /* For large copies we use 'movem' */ + + /* It is not optimal to tell the compiler about clobbering any + registers; that will move the saving/restoring of those registers + to the function prologue/epilogue, and make non-movem sizes + suboptimal. + + This method is not foolproof; it assumes that the "asm reg" + declarations at the beginning of the function really are used + here (beware: they may be moved to temporary registers). + This way, we do not have to save/move the registers around into + temporaries; we can safely use them straight away. + + If you want to check that the allocation was right; then + check the equalities in the first comment. It should say + "r13=r13, r12=r12, r11=r11" */ + __asm__ volatile (" + ;; Check that the following is true (same register names on + ;; both sides of equal sign, as in r8=r8): + ;; %0=r13, %1=r12, %4=r11 + ;; + ;; Save the registers we'll clobber in the movem process + ;; on the stack. Don't mention them to gcc, it will only be + ;; upset. + subq 11*4,$sp + movem $r10,[$sp] + + move.d $r11,$r0 + move.d $r11,$r1 + move.d $r11,$r2 + move.d $r11,$r3 + move.d $r11,$r4 + move.d $r11,$r5 + move.d $r11,$r6 + move.d $r11,$r7 + move.d $r11,$r8 + move.d $r11,$r9 + move.d $r11,$r10 + + ;; Now we've got this: + ;; r13 - dst + ;; r12 - n + + ;; Update n for the first loop + subq 12*4,$r12 +0: + subq 12*4,$r12 + bge 0b + movem $r11,[$r13+] + + addq 12*4,$r12 ;; compensate for last loop underflowing n + + ;; Restore registers from stack + movem [$sp+],$r10" + + /* Outputs */ : "=r" (dst), "=r" (n) + /* Inputs */ : "0" (dst), "1" (n), "r" (lc)); + + } + + /* Either we directly starts copying, using dword copying + in a loop, or we copy as much as possible with 'movem' + and then the last block (<44 bytes) is copied here. + This will work since 'movem' will have updated src,dst,n. */ + + while ( n >= 16 ) + { + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + n -= 16; + } + + /* A switch() is definitely the fastest although it takes a LOT of code. + * Particularly if you inline code this. + */ + switch (n) + { + case 0: + break; + case 1: + *(char*)dst = (char) lc; + break; + case 2: + *(short*)dst = (short) lc; + break; + case 3: + *((short*)dst)++ = (short) lc; + *(char*)dst = (char) lc; + break; + case 4: + *((long*)dst)++ = lc; + break; + case 5: + *((long*)dst)++ = lc; + *(char*)dst = (char) lc; + break; + case 6: + *((long*)dst)++ = lc; + *(short*)dst = (short) lc; + break; + case 7: + *((long*)dst)++ = lc; + *((short*)dst)++ = (short) lc; + *(char*)dst = (char) lc; + break; + case 8: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + break; + case 9: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *(char*)dst = (char) lc; + break; + case 10: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *(short*)dst = (short) lc; + break; + case 11: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((short*)dst)++ = (short) lc; + *(char*)dst = (char) lc; + break; + case 12: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + break; + case 13: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *(char*)dst = (char) lc; + break; + case 14: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *(short*)dst = (short) lc; + break; + case 15: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((short*)dst)++ = (short) lc; + *(char*)dst = (char) lc; + break; + } + } + + return return_dst; /* destination pointer. */ +} /* memset() */ diff --git a/arch/cris/arch-v10/lib/old_checksum.c b/arch/cris/arch-v10/lib/old_checksum.c new file mode 100644 index 00000000000..22a6f0aa9ce --- /dev/null +++ b/arch/cris/arch-v10/lib/old_checksum.c @@ -0,0 +1,85 @@ +/* $Id: old_checksum.c,v 1.3 2003/10/27 08:04:32 starvik Exp $ + * + * 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> + +unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) +{ + /* + * Experiments with ethernet and slip connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. + */ + const unsigned char *endMarker = buff + len; + const unsigned char *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 += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + } + marker = endMarker - (len % 2); + while(buff < marker) { + sum += *((unsigned short *)buff)++; + } + if(endMarker - buff > 0) { + sum += *buff; /* add extra byte seperately */ + } + BITOFF; + return(sum); +} + +EXPORT_SYMBOL(csum_partial); diff --git a/arch/cris/arch-v10/lib/string.c b/arch/cris/arch-v10/lib/string.c new file mode 100644 index 00000000000..8ffde4901b5 --- /dev/null +++ b/arch/cris/arch-v10/lib/string.c @@ -0,0 +1,225 @@ +/*#************************************************************************#*/ +/*#-------------------------------------------------------------------------*/ +/*# */ +/*# FUNCTION NAME: memcpy() */ +/*# */ +/*# PARAMETERS: void* dst; Destination address. */ +/*# void* src; Source address. */ +/*# int len; Number of bytes to copy. */ +/*# */ +/*# RETURNS: dst. */ +/*# */ +/*# DESCRIPTION: Copies len bytes of memory from src to dst. No guarantees */ +/*# about copying of overlapping memory areas. This routine is */ +/*# very sensitive to compiler changes in register allocation. */ +/*# Should really be rewritten to avoid this problem. */ +/*# */ +/*#-------------------------------------------------------------------------*/ +/*# */ +/*# HISTORY */ +/*# */ +/*# DATE NAME CHANGES */ +/*# ---- ---- ------- */ +/*# 941007 Kenny R Creation */ +/*# 941011 Kenny R Lots of optimizations and inlining. */ +/*# 941129 Ulf A Adapted for use in libc. */ +/*# 950216 HP N==0 forgotten if non-aligned src/dst. */ +/*# Added some optimizations. */ +/*# 001025 HP Make src and dst char *. Align dst to */ +/*# dword, not just word-if-both-src-and-dst- */ +/*# are-misaligned. */ +/*# */ +/*#-------------------------------------------------------------------------*/ + +#include <linux/types.h> + +void *memcpy(void *pdst, + const void *psrc, + size_t pn) +{ + /* Ok. Now we want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. + As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop). + + If gcc was allright, it really would need no temporaries, and no + stack space to save stuff on. */ + + register void *return_dst __asm__ ("r10") = pdst; + register char *dst __asm__ ("r13") = pdst; + register const char *src __asm__ ("r11") = psrc; + register int n __asm__ ("r12") = pn; + + + /* When src is aligned but not dst, this makes a few extra needless + cycles. I believe it would take as many to check that the + re-alignment was unnecessary. */ + if (((unsigned long) dst & 3) != 0 + /* Don't align if we wouldn't copy more than a few bytes; so we + don't have to check further for overflows. */ + && n >= 3) + { + if ((unsigned long) dst & 1) + { + n--; + *(char*)dst = *(char*)src; + src++; + dst++; + } + + if ((unsigned long) dst & 2) + { + n -= 2; + *(short*)dst = *(short*)src; + src += 2; + dst += 2; + } + } + + /* Decide which copying method to use. */ + 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 (" + ;; Check that the following is true (same register names on + ;; both sides of equal sign, as in r8=r8): + ;; %0=r13, %1=r11, %2=r12 + ;; + ;; Save the registers we'll use in the movem process + ;; on the stack. + subq 11*4,$sp + movem $r10,[$sp] + + ;; Now we've got this: + ;; r11 - src + ;; r13 - dst + ;; r12 - n + + ;; Update n for the first loop + subq 44,$r12 +0: + movem [$r11+],$r10 + subq 44,$r12 + bge 0b + movem $r10,[$r13+] + + addq 44,$r12 ;; compensate for last loop underflowing n + + ;; Restore registers from stack + movem [$sp+],$r10" + + /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n) + /* Inputs */ : "0" (dst), "1" (src), "2" (n)); + + } + + /* Either we directly starts copying, using dword copying + in a loop, or we copy as much as possible with 'movem' + and then the last block (<44 bytes) is copied here. + This will work since 'movem' will have updated src,dst,n. */ + + while ( n >= 16 ) + { + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + n -= 16; + } + + /* A switch() is definitely the fastest although it takes a LOT of code. + * Particularly if you inline code this. + */ + switch (n) + { + case 0: + break; + case 1: + *(char*)dst = *(char*)src; + break; + case 2: + *(short*)dst = *(short*)src; + break; + case 3: + *((short*)dst)++ = *((short*)src)++; + *(char*)dst = *(char*)src; + break; + case 4: + *((long*)dst)++ = *((long*)src)++; + break; + case 5: + *((long*)dst)++ = *((long*)src)++; + *(char*)dst = *(char*)src; + break; + case 6: + *((long*)dst)++ = *((long*)src)++; + *(short*)dst = *(short*)src; + break; + case 7: + *((long*)dst)++ = *((long*)src)++; + *((short*)dst)++ = *((short*)src)++; + *(char*)dst = *(char*)src; + break; + case 8: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + break; + case 9: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *(char*)dst = *(char*)src; + break; + case 10: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *(short*)dst = *(short*)src; + break; + case 11: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((short*)dst)++ = *((short*)src)++; + *(char*)dst = *(char*)src; + break; + case 12: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + break; + case 13: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *(char*)dst = *(char*)src; + break; + case 14: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *(short*)dst = *(short*)src; + break; + case 15: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((short*)dst)++ = *((short*)src)++; + *(char*)dst = *(char*)src; + break; + } + + return return_dst; /* destination pointer. */ +} /* memcpy() */ diff --git a/arch/cris/arch-v10/lib/usercopy.c b/arch/cris/arch-v10/lib/usercopy.c new file mode 100644 index 00000000000..43778d53c25 --- /dev/null +++ b/arch/cris/arch-v10/lib/usercopy.c @@ -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 allright, it really would need no temporaries, and no + stack space to save stuff on. */ + + register char *dst __asm__ ("r13") = pdst; + register const char *src __asm__ ("r11") = psrc; + register int n __asm__ ("r12") = pn; + register int retn __asm__ ("r10") = 0; + + + /* When src is aligned but not dst, this makes a few extra needless + cycles. I believe it would take as many to check that the + re-alignment was unnecessary. */ + if (((unsigned long) dst & 3) != 0 + /* Don't align if we wouldn't copy more than a few bytes; so we + don't have to check further for overflows. */ + && n >= 3) + { + if ((unsigned long) dst & 1) + { + __asm_copy_to_user_1 (dst, src, retn); + n--; + } + + if ((unsigned long) dst & 2) + { + __asm_copy_to_user_2 (dst, src, retn); + n -= 2; + } + } + + /* 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\ + + ;; Save the registers we'll use in the movem process + ;; on the stack. + subq 11*4,$sp + movem $r10,[$sp] + + ;; Now we've got this: + ;; r11 - src + ;; r13 - dst + ;; r12 - n + + ;; Update n for the first loop + subq 44,$r12 + +; Since the noted PC of a faulting instruction in a delay-slot of a taken +; branch, is that of the branch target, we actually point at the from-movem +; for this case. There is no ambiguity here; if there was a fault in that +; instruction (meaning a kernel oops), the faulted PC would be the address +; after *that* movem. + +0: + movem [$r11+],$r10 + subq 44,$r12 + bge 0b + movem $r10,[$r13+] +1: + addq 44,$r12 ;; compensate for last loop underflowing n + + ;; Restore registers from stack + movem [$sp+],$r10 +2: + .section .fixup,\"ax\" + +; To provide a correct count in r10 of bytes that failed to be copied, +; we jump back into the loop if the loop-branch was taken. There is no +; performance penalty for sany use; the program will segfault soon enough. + +3: + move.d [$sp],$r10 + addq 44,$r10 + move.d $r10,[$sp] + jump 0b +4: + movem [$sp+],$r10 + addq 44,$r10 + addq 44,$r12 + jump 2b + + .previous + .section __ex_table,\"a\" + .dword 0b,3b + .dword 1b,4b + .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 __user *pdst, const void *psrc, unsigned long pn) +{ + /* We want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. + As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop). + + FIXME: Comment for old gcc version. Check. + If gcc was allright, it really would need no temporaries, and no + stack space to save stuff on. */ + + register char *dst __asm__ ("r13") = pdst; + register const char *src __asm__ ("r11") = psrc; + register int n __asm__ ("r12") = pn; + register int retn __asm__ ("r10") = 0; + + /* The best reason to align src is that we then know that a read-fault + was for aligned bytes; there's no 1..3 remaining good bytes to + pickle. */ + if (((unsigned long) src & 3) != 0) + { + if (((unsigned long) src & 1) && n != 0) + { + __asm_copy_from_user_1 (dst, src, retn); + n--; + } + + if (((unsigned long) src & 2) && n >= 2) + { + __asm_copy_from_user_2 (dst, src, retn); + n -= 2; + } + + /* We only need one check after the unalignment-adjustments, because + if both adjustments were done, either both or neither reference + had an exception. */ + if (retn != 0) + goto copy_exception_bytes; + } + + /* 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 (" + .ifnc %0%1%2%3,$r13$r11$r12$r10 \n\ + .err \n\ + .endif \n\ + + ;; Save the registers we'll use in the movem process + ;; on the stack. + subq 11*4,$sp + movem $r10,[$sp] + + ;; Now we've got this: + ;; r11 - src + ;; r13 - dst + ;; r12 - n + + ;; Update n for the first loop + subq 44,$r12 +0: + movem [$r11+],$r10 +1: + subq 44,$r12 + bge 0b + movem $r10,[$r13+] + + addq 44,$r12 ;; compensate for last loop underflowing n + + ;; Restore registers from stack + movem [$sp+],$r10 +4: + .section .fixup,\"ax\" + +;; Do not jump back into the loop if we fail. For some uses, we get a +;; page fault somewhere on the line. Without checking for page limits, +;; we don't know where, but we need to copy accurately and keep an +;; accurate count; not just clear the whole line. To do that, we fall +;; down in the code below, proceeding with smaller amounts. It should +;; be kept in mind that we have to cater to code like what at one time +;; was in fs/super.c: +;; i = size - copy_from_user((void *)page, data, size); +;; which would cause repeated faults while clearing the remainder of +;; the SIZE bytes at PAGE after the first fault. +;; A caveat here is that we must not fall through from a failing page +;; to a valid page. + +3: + movem [$sp+],$r10 + addq 44,$r12 ;; Get back count before faulting point. + subq 44,$r11 ;; Get back pointer to faulting movem-line. + jump 4b ;; Fall through, pretending the fault didn't happen. + + .previous + .section __ex_table,\"a\" + .dword 1b,3b + .previous" + + /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn) + /* Inputs */ : "0" (dst), "1" (src), "2" (n), "3" (retn)); + + } + + /* Either we directly start copying here, using dword copying in a loop, + or we copy as much as possible with 'movem' and then the last block + (<44 bytes) is copied here. This will work since 'movem' will have + updated src, dst and n. (Except with failing src.) + + Since we want to keep src accurate, we can't use + __asm_copy_from_user_N with N != (1, 2, 4); it updates dst and + retn, but not src (by design; it's value is ignored elsewhere). */ + + while (n >= 4) + { + __asm_copy_from_user_4 (dst, src, retn); + n -= 4; + + if (retn) + goto copy_exception_bytes; + } + + /* If we get here, there were no memory read faults. */ + switch (n) + { + /* These copies are at least "naturally aligned" (so we don't have + to check each byte), due to the src alignment code before the + movem loop. The *_3 case *will* get the correct count for retn. */ + case 0: + /* This case deliberately left in (if you have doubts check the + generated assembly code). */ + break; + case 1: + __asm_copy_from_user_1 (dst, src, retn); + break; + case 2: + __asm_copy_from_user_2 (dst, src, retn); + break; + case 3: + __asm_copy_from_user_3 (dst, src, retn); + break; + } + + /* If we get here, retn correctly reflects the number of failing + bytes. */ + return retn; + +copy_exception_bytes: + /* We already have "retn" bytes cleared, and need to clear the + remaining "n" bytes. A non-optimized simple byte-for-byte in-line + memset is preferred here, since this isn't speed-critical code and + we'd rather have this a leaf-function than calling memset. */ + { + char *endp; + for (endp = dst + n; dst < endp; dst++) + *dst = 0; + } + + return retn + n; +} + +/* Zero userspace. */ + +unsigned long +__do_clear_user (void __user *pto, unsigned long pn) +{ + /* We want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. + As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop). + + FIXME: Comment for old gcc version. Check. + If gcc was allright, it really would need no temporaries, and no + stack space to save stuff on. */ + + register char *dst __asm__ ("r13") = pto; + register int n __asm__ ("r12") = pn; + register int retn __asm__ ("r10") = 0; + + + if (((unsigned long) dst & 3) != 0 + /* Don't align if we wouldn't copy more than a few bytes. */ + && n >= 3) + { + if ((unsigned long) dst & 1) + { + __asm_clear_1 (dst, retn); + n--; + } + + if ((unsigned long) dst & 2) + { + __asm_clear_2 (dst, retn); + n -= 2; + } + } + + /* Decide which copying method to use. + FIXME: This number is from the "ordinary" kernel memset. */ + if (n >= (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 (" + .ifnc %0%1%2,$r13$r12$r10 \n\ + .err \n\ + .endif \n\ + + ;; Save the registers we'll clobber in the movem process + ;; on the stack. Don't mention them to gcc, it will only be + ;; upset. + subq 11*4,$sp + movem $r10,[$sp] + + clear.d $r0 + clear.d $r1 + clear.d $r2 + clear.d $r3 + clear.d $r4 + clear.d $r5 + clear.d $r6 + clear.d $r7 + clear.d $r8 + clear.d $r9 + clear.d $r10 + clear.d $r11 + + ;; Now we've got this: + ;; r13 - dst + ;; r12 - n + + ;; Update n for the first loop + subq 12*4,$r12 +0: + subq 12*4,$r12 + bge 0b + movem $r11,[$r13+] +1: + addq 12*4,$r12 ;; compensate for last loop underflowing n + + ;; Restore registers from stack + movem [$sp+],$r10 +2: + .section .fixup,\"ax\" +3: + move.d [$sp],$r10 + addq 12*4,$r10 + move.d $r10,[$sp] + clear.d $r10 + jump 0b + +4: + movem [$sp+],$r10 + addq 12*4,$r10 + addq 12*4,$r12 + jump 2b + + .previous + .section __ex_table,\"a\" + .dword 0b,3b + .dword 1b,4b + .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; +} diff --git a/arch/cris/arch-v10/mm/Makefile b/arch/cris/arch-v10/mm/Makefile new file mode 100644 index 00000000000..588b4baee85 --- /dev/null +++ b/arch/cris/arch-v10/mm/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the linux cris-specific parts of the memory manager. +# + +obj-y := fault.o init.o tlb.o + diff --git a/arch/cris/arch-v10/mm/fault.c b/arch/cris/arch-v10/mm/fault.c new file mode 100644 index 00000000000..6805cdb25a5 --- /dev/null +++ b/arch/cris/arch-v10/mm/fault.c @@ -0,0 +1,117 @@ +/* + * linux/arch/cris/mm/fault.c + * + * Low level bus fault handler + * + * + * Copyright (C) 2000, 2001 Axis Communications AB + * + * Authors: Bjorn Wesen + * + */ + +#include <linux/mm.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/arch/svinto.h> + +/* debug of low-level TLB reload */ +#undef DEBUG + +#ifdef DEBUG +#define D(x) x +#else +#define D(x) +#endif + +extern volatile pgd_t *current_pgd; + +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*)current_pgd; + 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_save_flags(flags); + local_irq_disable(); + pmd = (pmd_t *)(pgd + pgd_index(address)); + if (pmd_none(*pmd)) + return; + pte = *pte_offset_kernel(pmd, address); + if (!pte_present(pte)) + return; + *R_TLB_SELECT = select; + *R_TLB_HI = cause; + *R_TLB_LO = pte_val(pte); + local_irq_restore(flags); +} + +/* Called from arch/cris/mm/fault.c to find fixup code. */ +int +find_fixup_code(struct pt_regs *regs) +{ + const struct exception_table_entry *fixup; + + if ((fixup = search_exception_tables(regs->irp)) != 0) { + /* Adjust the instruction pointer in the stackframe. */ + regs->irp = fixup->fixup; + + /* + * Don't return by restoring the CPU state, so switch + * frame-type. + */ + regs->frametype = CRIS_FRAME_NORMAL; + return 1; + } + + return 0; +} diff --git a/arch/cris/arch-v10/mm/init.c b/arch/cris/arch-v10/mm/init.c new file mode 100644 index 00000000000..a9f975a9cfb --- /dev/null +++ b/arch/cris/arch-v10/mm/init.c @@ -0,0 +1,264 @@ +/* + * linux/arch/cris/arch-v10/mm/init.c + * + */ +#include <linux/config.h> +#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 <asm/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) + */ + + current_pgd = 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, &contig_page_data, 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 occationally 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); +} diff --git a/arch/cris/arch-v10/mm/tlb.c b/arch/cris/arch-v10/mm/tlb.c new file mode 100644 index 00000000000..9d06125ff5a --- /dev/null +++ b/arch/cris/arch-v10/mm/tlb.c @@ -0,0 +1,248 @@ +/* + * linux/arch/cris/arch-v10/mm/tlb.c + * + * Low level TLB handling + * + * + * Copyright (C) 2000-2002 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + */ + +#include <asm/tlb.h> +#include <asm/mmu_context.h> +#include <asm/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_save_flags(flags); + local_irq_disable(); + 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_save_flags(flags); + local_irq_disable(); + 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_save_flags(flags); + local_irq_disable(); + 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); +} + +/* invalidate a page range */ + +void +flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, + unsigned long end) +{ + struct mm_struct *mm = vma->vm_mm; + int page_id = mm->context.page_id; + int i; + unsigned long flags; + + D(printk("tlb: flush range %p<->%p in context %d (%p)\n", + start, end, page_id, mm)); + + if(page_id == NO_CONTEXT) + return; + + start &= PAGE_MASK; /* probably not necessary */ + end &= PAGE_MASK; /* dito */ + + /* invalidate those TLB entries that match both the mm context + * and the virtual address range + */ + + local_save_flags(flags); + local_irq_disable(); + for(i = 0; i < NUM_TLB_ENTRIES; i++) { + unsigned long tlb_hi, vpn; + *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i); + tlb_hi = *R_TLB_HI; + vpn = tlb_hi & PAGE_MASK; + if (IO_EXTRACT(R_TLB_HI, page_id, tlb_hi) == page_id && + vpn >= start && vpn < end) { + *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | + IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); + + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | + IO_STATE(R_TLB_LO, valid, no ) | + IO_STATE(R_TLB_LO, kernel,no ) | + IO_STATE(R_TLB_LO, we, no ) | + IO_FIELD(R_TLB_LO, pfn, 0 ) ); + } + } + local_irq_restore(flags); +} + +/* dump the entire TLB for debug purposes */ + +#if 0 +void +dump_tlb_all(void) +{ + int i; + unsigned long flags; + + printk("TLB dump. LO is: pfn | reserved | global | valid | kernel | we |\n"); + + local_save_flags(flags); + local_irq_disable(); + for(i = 0; i < NUM_TLB_ENTRIES; i++) { + *R_TLB_SELECT = ( IO_FIELD(R_TLB_SELECT, index, i) ); + printk("Entry %d: HI 0x%08lx, LO 0x%08lx\n", + i, *R_TLB_HI, *R_TLB_LO); + } + local_irq_restore(flags); +} +#endif + +/* + * 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) +{ + /* 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. + */ + + current_pgd = next->pgd; + + /* switch context in the MMU */ + + D(printk("switching mmu_context to %d (%p)\n", next->context, next)); + + *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT, page_id, next->context.page_id); +} + diff --git a/arch/cris/arch-v10/output_arch.ld b/arch/cris/arch-v10/output_arch.ld new file mode 100644 index 00000000000..2f328800699 --- /dev/null +++ b/arch/cris/arch-v10/output_arch.ld @@ -0,0 +1,2 @@ +/* At the time of this writing, there's no equivalent ld option. */ +OUTPUT_ARCH (cris) diff --git a/arch/cris/arch-v10/vmlinux.lds.S b/arch/cris/arch-v10/vmlinux.lds.S new file mode 100644 index 00000000000..71ba736be8f --- /dev/null +++ b/arch/cris/arch-v10/vmlinux.lds.S @@ -0,0 +1,120 @@ +/* ld script to make the Linux/CRIS kernel + * Authors: Bjorn Wesen (bjornw@axis.com) + * + * It is VERY DANGEROUS to fiddle around with the symbols in this + * script. It is for example quite vital that all generated sections + * that are used are actually named here, otherwise the linker will + * put them at the end, where the init stuff is which is FREED after + * the kernel has booted. + */ + +#include <linux/config.h> +#include <asm-generic/vmlinux.lds.h> + +jiffies = jiffies_64; +SECTIONS +{ + . = DRAM_VIRTUAL_BASE; + dram_start = .; + ibr_start = .; + . = . + 0x4000; /* see head.S and pages reserved at the start */ + + _text = .; /* Text and read-only data */ + text_start = .; /* lots of aliases */ + _stext = .; + __stext = .; + .text : { + *(.text) + SCHED_TEXT + LOCK_TEXT + *(.fixup) + *(.text.__*) + } + + _etext = . ; /* End of text section */ + __etext = .; + + . = ALIGN(4); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + RODATA + + . = ALIGN (4); + ___data_start = . ; + __Sdata = . ; + .data : { /* Data */ + *(.data) + } + __edata = . ; /* End of data section */ + _edata = . ; + + . = ALIGN(8192); /* init_task and stack, must be aligned */ + .data.init_task : { *(.data.init_task) } + + . = ALIGN(8192); /* Init code and data */ + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + .init.data : { *(.init.data) } + . = ALIGN(16); + __setup_start = .; + .init.setup : { *(.init.setup) } + __setup_end = .; + .initcall.init : { + __initcall_start = .; + *(.initcall1.init); + *(.initcall2.init); + *(.initcall3.init); + *(.initcall4.init); + *(.initcall5.init); + *(.initcall6.init); + *(.initcall7.init); + __initcall_end = .; + } + + .con_initcall.init : { + __con_initcall_start = .; + *(.con_initcall.init) + __con_initcall_end = .; + } + SECURITY_INIT + + .init.ramfs : { + __initramfs_start = .; + *(.init.ramfs) + __initramfs_end = .; + /* We fill to the next page, so we can discard all init + pages without needing to consider what payload might be + appended to the kernel image. */ + FILL (0); + . = ALIGN (8192); + } + + __vmlinux_end = .; /* last address of the physical file */ + __init_end = .; + + __data_end = . ; /* Move to _edata ? */ + __bss_start = .; /* BSS */ + .bss : { + *(COMMON) + *(.bss) + } + + . = ALIGN (0x20); + _end = .; + __end = .; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.text.exit) + *(.data.exit) + *(.exitcall.exit) + } + + dram_end = dram_start + CONFIG_ETRAX_DRAM_SIZE*1024*1024; +} |