## Automatically generated incremental diff ## From: linux-2.4.21-bk5 ## To: linux-2.4.21-bk6 ## Robot: $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $ diff -urN linux-2.4.21-bk5/Documentation/Configure.help linux-2.4.21-bk6/Documentation/Configure.help --- linux-2.4.21-bk5/Documentation/Configure.help 2003-07-10 02:54:54.000000000 -0700 +++ linux-2.4.21-bk6/Documentation/Configure.help 2003-07-10 02:54:58.000000000 -0700 @@ -15938,30 +15938,6 @@ If unsure, say N. -Allow direct I/O on files in NFS -CONFIG_NFS_DIRECTIO - There are important applications whose performance or correctness - depends on uncached access to file data. Database clusters (multiple - copies of the same instance running on separate hosts) implement their - own cache coherency protocol that subsumes the NFS cache protocols. - Applications that process datasets considerably larger than the client's - memory do not always benefit from a local cache. A streaming video - server, for instance, has no need to cache the contents of a file. - - This option enables applications to perform direct I/O on files in NFS - file systems using the O_DIRECT open() flag. When O_DIRECT is set for - files, their data is not cached in the system's page cache. Direct - read and write operations are aligned to block boundaries. Data is - moved to and from user-level application buffers directly. - - Unless your program is designed to use O_DIRECT properly, you are much - better off allowing the NFS client to manage caching for you. Misusing - O_DIRECT can cause poor server performance or network storms. This - kernel build option defaults OFF to avoid exposing system administrators - unwittingly to a potentially hazardous feature. - - If unsure, say N. - Root file system on NFS CONFIG_ROOT_NFS If you want your Linux box to mount its whole root file system (the @@ -16118,7 +16094,7 @@ say M here and read . If unsure, say N. -Apple Macintosh file system support +Apple HFS file system support CONFIG_HFS_FS If you say Y here, you will be able to mount Macintosh-formatted floppy disks and hard drive partitions with full read-write access. @@ -16131,6 +16107,21 @@ compile it as a module, say M here and read . +Apple HFS+ (Extended HFS) file system support +CONFIG_HFSPLUS_FS + If you say Y here, you will be able to mount extended format + Macintosh-formatted hard drive partitions with full read-write access. + + This file system is often called HFS+ and was introduced with + MacOS 8. It includes all Mac specific filesystem data such as + data forks and creator codes, but it also has several UNIX + style features such as file ownership and permissions. + + This file system is also available as a module ( = code which can + be inserted in and removed from the running kernel whenever you + want). The module is called hfsplus.o. If you want to compile it + as a module, say M here and read Documentation/modules.txt. + ROM file system support CONFIG_ROMFS_FS This is a very small read-only file system mainly intended for diff -urN linux-2.4.21-bk5/Documentation/kernel-parameters.txt linux-2.4.21-bk6/Documentation/kernel-parameters.txt --- linux-2.4.21-bk5/Documentation/kernel-parameters.txt 2003-07-10 02:54:54.000000000 -0700 +++ linux-2.4.21-bk6/Documentation/kernel-parameters.txt 2003-07-10 02:54:58.000000000 -0700 @@ -503,7 +503,7 @@ plip= [PPT,NET] Parallel port network link. profile= [KNL] enable kernel profiling via /proc/profile - (param:log level). + (param: profile step/bucket size as a power of 2) prompt_ramdisk= [RAM] List of RAM disks to prompt for floppy disk before loading. diff -urN linux-2.4.21-bk5/Makefile linux-2.4.21-bk6/Makefile --- linux-2.4.21-bk5/Makefile 2003-07-10 02:54:54.000000000 -0700 +++ linux-2.4.21-bk6/Makefile 2003-07-10 02:54:58.000000000 -0700 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 21 -EXTRAVERSION = -bk5 +EXTRAVERSION = -bk6 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff -urN linux-2.4.21-bk5/arch/cris/Makefile linux-2.4.21-bk6/arch/cris/Makefile --- linux-2.4.21-bk5/arch/cris/Makefile 2002-08-02 17:39:42.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/Makefile 2003-07-10 02:54:58.000000000 -0700 @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.27 2002/03/08 13:08:50 bjornw Exp $ +# # cris/Makefile # # This file is included by the global makefile so that you can add your own @@ -56,6 +56,10 @@ # each others config options SUBDIRS += arch/cris/boot/rescue endif + +# Note that the order of .o files below is important is some cases +# (because initcalls are executed in the order they are linked) +# Never add any driver before mm.o! CORE_FILES += arch/cris/kernel/kernel.o arch/cris/mm/mm.o DRIVERS += arch/cris/drivers/drivers.o LIBGCC = $(shell $(CC) $(CFLAGS) -print-file-name=libgcc.a) diff -urN linux-2.4.21-bk5/arch/cris/boot/compressed/README linux-2.4.21-bk6/arch/cris/boot/compressed/README --- linux-2.4.21-bk5/arch/cris/boot/compressed/README 2001-02-08 16:32:44.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/boot/compressed/README 2003-07-10 02:54:58.000000000 -0700 @@ -1,6 +1,5 @@ Creation of the self-extracting compressed kernel image (vmlinuz) ----------------------------------------------------------------- -$Id: README,v 1.1 2000/11/22 17:20:46 bjornw Exp $ This can be slightly confusing because it's a process with many steps. diff -urN linux-2.4.21-bk5/arch/cris/boot/compressed/misc.c linux-2.4.21-bk6/arch/cris/boot/compressed/misc.c --- linux-2.4.21-bk5/arch/cris/boot/compressed/misc.c 2001-05-01 16:04:56.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/boot/compressed/misc.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,19 +1,17 @@ /* * misc.c * - * $Id: misc.c,v 1.6 2001/04/09 10:00:21 starvik Exp $ - * - * This is a collection of several routines from gzip-1.0.3 + * 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 adress we use when we network load an uncompressed + * 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 etrax100.ld. */ @@ -65,8 +63,8 @@ #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ #define RESERVED 0xC0 /* bit 6,7: reserved */ -#define get_byte() inbuf[inptr++] - +#define get_byte() inbuf[inptr++] + /* Diagnostic functions */ #ifdef DEBUG # define Assert(cond,msg) {if(!(cond)) error(msg);} @@ -95,20 +93,20 @@ 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) @@ -196,9 +194,9 @@ ulg c = crc; /* temporary variable */ unsigned n; uch *in, *out, ch; - + in = window; - out = &output_data[output_ptr]; + out = &output_data[output_ptr]; for (n = 0; n < outcnt; n++) { ch = *out++ = *in++; c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); @@ -229,7 +227,7 @@ decompress_kernel() { char revision; - + /* input_data is set in head.S */ inbuf = input_data; diff -urN linux-2.4.21-bk5/arch/cris/boot/rescue/head.S linux-2.4.21-bk6/arch/cris/boot/rescue/head.S --- linux-2.4.21-bk5/arch/cris/boot/rescue/head.S 2002-02-25 11:37:52.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/boot/rescue/head.S 2003-07-10 02:54:58.000000000 -0700 @@ -1,5 +1,4 @@ -/* $Id: head.S,v 1.10 2001/11/08 15:10:16 starvik 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. @@ -52,7 +51,7 @@ * * 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 its data without updating the checksums. A 1 in bit 0 + * 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. @@ -60,7 +59,7 @@ * During the wait for serial input, the status LED will flash so the * user knows something went wrong. * - * Copyright (C) 1999,2001 Axis Communications AB + * Copyright (C) 1999, 2000, 2001, 2002, 2003 Axis Communications AB */ #include @@ -130,7 +129,7 @@ ;; 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 - ;; its not -1, we jump using the value as jump target. since we can always + ;; 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. @@ -150,7 +149,20 @@ move.d 0x3, $r0 ; enable = on, phy = mii_clk move.d $r0, [R_NETWORK_GEN_CONFIG] #endif + + ;; 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" @@ -192,7 +204,7 @@ moveq -1, $r7 ploop: move.d [$r3+], $r1 ; partition offset (from ptable start) - bne notfirst ; check if its the partition containing ptable + 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 @@ -226,18 +238,6 @@ ;; 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 diff -urN linux-2.4.21-bk5/arch/cris/boot/rescue/kimagerescue.S linux-2.4.21-bk6/arch/cris/boot/rescue/kimagerescue.S --- linux-2.4.21-bk5/arch/cris/boot/rescue/kimagerescue.S 2001-10-08 11:43:54.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/boot/rescue/kimagerescue.S 2003-07-10 02:54:58.000000000 -0700 @@ -1,5 +1,4 @@ -/* $Id: kimagerescue.S,v 1.5 2001/10/03 17:15:15 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 diff -urN linux-2.4.21-bk5/arch/cris/boot/rescue/testrescue.S linux-2.4.21-bk6/arch/cris/boot/rescue/testrescue.S --- linux-2.4.21-bk5/arch/cris/boot/rescue/testrescue.S 2001-10-08 11:43:54.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/boot/rescue/testrescue.S 2003-07-10 02:54:58.000000000 -0700 @@ -1,9 +1,8 @@ -/* $Id: testrescue.S,v 1.3 2001/10/03 17:15:15 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 + * + * Copyright (C) 1999-2003 Axis Communications AB */ #define ASSEMBLER_MACROS_ONLY @@ -16,11 +15,9 @@ moveq -1, $r2 move.b $r2, [R_PORT_PA_DIR] moveq 0, $r2 - move.b $r2, [R_PORT_PA_DATA] + move.b $r2, [R_PORT_PA_DATA] endless: nop ba endless nop - - diff -urN linux-2.4.21-bk5/arch/cris/config.in linux-2.4.21-bk6/arch/cris/config.in --- linux-2.4.21-bk5/arch/cris/config.in 2003-07-10 02:54:54.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/config.in 2003-07-10 02:54:58.000000000 -0700 @@ -32,14 +32,27 @@ tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +string 'Kernel command line' CONFIG_ETRAX_CMDLINE "root=/dev/mtdblock3" + bool 'Use kernel gdb debugger' CONFIG_ETRAX_KGDB -bool 'Enable Etrax100 watchdog' CONFIG_ETRAX_WATCHDOG +bool 'Use Artpec gdb debugger' CONFIG_ETRAX_AGDB + +bool 'Enable ETRAX watchdog' CONFIG_ETRAX_WATCHDOG if [ "$CONFIG_ETRAX_WATCHDOG" = "y" ]; then - bool 'Disable watchdog during Oops printouts' CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + bool ' Disable watchdog during Oops printouts' CONFIG_ETRAX_WATCHDOG_NICE_DOGGY fi +bool 'Enable ETRAX interrupt debugging' CONFIG_ETRAX_DEBUG_INTERRUPT + bool 'Enable ETRAX fast timer API' CONFIG_ETRAX_FAST_TIMER +if [ "$CONFIG_ETRAX_FAST_TIMER" = "y" ]; then + bool ' Enable fast timer logging' CONFIG_ETRAX_FAST_TIMER_LOGGING + bool ' Enable fast timer debug' CONFIG_ETRAX_FAST_TIMER_DEBUG + bool ' Enable fast timer sanity checks' CONFIG_ETRAX_FAST_TIMER_SANITY_CHECKS +fi + +bool 'Enable reboot at out of memory' CONFIG_OOM_REBOOT endmenu @@ -51,7 +64,7 @@ Etrax-100-LX-v2 CONFIG_ETRAX100LX_V2 \ Etrax-100-LX-for-xsim-simulator CONFIG_SVINTO_SIM" Etrax-100-LX-v1 -# Etrax100 LX v1 has a MMU "feature" requiring a low mapping +# ETRAX 100LX v1 has a MMU "feature" requiring a low mapping if [ "$CONFIG_ETRAX100LX" = "y" ]; then define_bool CONFIG_CRIS_LOW_MAP y @@ -64,8 +77,6 @@ int 'Buswidth of flash in bytes' CONFIG_ETRAX_FLASH_BUSWIDTH 2 -string 'Root device name' CONFIG_ETRAX_ROOT_DEVICE "/dev/mtdblock3" - choice 'Product LED port' \ "Port-PA-LEDs CONFIG_ETRAX_PA_LEDS \ Port-PB-LEDs CONFIG_ETRAX_PB_LEDS \ @@ -73,36 +84,36 @@ None CONFIG_ETRAX_NO_LEDS" Port-PA-LEDs if [ "$CONFIG_ETRAX_NO_LEDS" != "y" ]; then - int ' First green LED bit' CONFIG_ETRAX_LED1G 2 - int ' First red LED bit' CONFIG_ETRAX_LED1R 3 - int ' Second green LED bit' CONFIG_ETRAX_LED2G 4 - int ' Second red LED bit' CONFIG_ETRAX_LED2R 5 - int ' Third green LED bit' CONFIG_ETRAX_LED3G 2 - int ' Third red LED bit' CONFIG_ETRAX_LED3R 2 + int ' First green LED bit' CONFIG_ETRAX_LED1G 2 + int ' First red LED bit' CONFIG_ETRAX_LED1R 3 + int ' Second green LED bit' CONFIG_ETRAX_LED2G 4 + int ' Second red LED bit' CONFIG_ETRAX_LED2R 5 + int ' Third green LED bit' CONFIG_ETRAX_LED3G 2 + int ' Third red LED bit' CONFIG_ETRAX_LED3R 2 fi if [ "$CONFIG_ETRAX_CSP0_LEDS" = "y" ]; then - int ' Fourth red LED bit' CONFIG_ETRAX_LED4R 2 - int ' Fourth green LED bit' CONFIG_ETRAX_LED4G 2 - int ' Fifth red LED bit' CONFIG_ETRAX_LED5R 2 - int ' Fifth green LED bit' CONFIG_ETRAX_LED5G 2 - int ' Sixth red LED bit' CONFIG_ETRAX_LED6R 2 - int ' Sixth green LED bit' CONFIG_ETRAX_LED6G 2 - int ' Seventh red LED bit' CONFIG_ETRAX_LED7R 2 - int ' Seventh green LED bit' CONFIG_ETRAX_LED7G 2 - int ' Eigth yellow LED bit' CONFIG_ETRAX_LED8Y 2 - int ' Ninth yellow LED bit' CONFIG_ETRAX_LED9Y 2 - int ' Tenth yellow LED bit' CONFIG_ETRAX_LED10Y 2 - int ' Eleventh yellow LED bit' CONFIG_ETRAX_LED11Y 2 - int ' Twelfth red LED bit' CONFIG_ETRAX_LED12R 2 -fi + int ' Fourth red LED bit' CONFIG_ETRAX_LED4R 2 + int ' Fourth green LED bit' CONFIG_ETRAX_LED4G 2 + int ' Fifth red LED bit' CONFIG_ETRAX_LED5R 2 + int ' Fifth green LED bit' CONFIG_ETRAX_LED5G 2 + int ' Sixth red LED bit' CONFIG_ETRAX_LED6R 2 + int ' Sixth green LED bit' CONFIG_ETRAX_LED6G 2 + int ' Seventh red LED bit' CONFIG_ETRAX_LED7R 2 + int ' Seventh green LED bit' CONFIG_ETRAX_LED7G 2 + int ' Eigth yellow LED bit' CONFIG_ETRAX_LED8Y 2 + int ' Ninth yellow LED bit' CONFIG_ETRAX_LED9Y 2 + int ' Tenth yellow LED bit' CONFIG_ETRAX_LED10Y 2 + int ' Eleventh yellow LED bit' CONFIG_ETRAX_LED11Y 2 + int ' Twelfth red LED bit' CONFIG_ETRAX_LED12R 2 +fi choice 'Product debug-port' \ "Serial-0 CONFIG_ETRAX_DEBUG_PORT0 \ Serial-1 CONFIG_ETRAX_DEBUG_PORT1 \ Serial-2 CONFIG_ETRAX_DEBUG_PORT2 \ Serial-3 CONFIG_ETRAX_DEBUG_PORT3 \ - disabled CONFIG_ETRAX_DEBUG_PORT_NULL" Serial-0 + disabled CONFIG_ETRAX_DEBUG_PORT_NULL" Serial-0 choice 'Product rescue-port' \ "Serial-0 CONFIG_ETRAX_RESCUE_SER0 \ @@ -115,13 +126,13 @@ bool 'SDRAM support' CONFIG_ETRAX_SDRAM if [ "$CONFIG_ETRAX_SDRAM" = "n" ]; then - hex 'R_DRAM_CONFIG' CONFIG_ETRAX_DEF_R_DRAM_CONFIG 1a200040 - hex 'R_DRAM_TIMING' CONFIG_ETRAX_DEF_R_DRAM_TIMING 5611 + hex ' R_DRAM_CONFIG' CONFIG_ETRAX_DEF_R_DRAM_CONFIG 1a200040 + hex ' R_DRAM_TIMING' CONFIG_ETRAX_DEF_R_DRAM_TIMING 5611 fi if [ "$CONFIG_ETRAX_SDRAM" = "y" ]; then - hex 'R_SDRAM_CONFIG' CONFIG_ETRAX_DEF_R_SDRAM_CONFIG d2fa7878 - hex 'R_SDRAM_TIMING' CONFIG_ETRAX_DEF_R_SDRAM_TIMING 80004801 + hex ' R_SDRAM_CONFIG' CONFIG_ETRAX_DEF_R_SDRAM_CONFIG d2fa7878 + hex ' R_SDRAM_TIMING' CONFIG_ETRAX_DEF_R_SDRAM_TIMING 80004801 fi hex 'R_PORT_PA_DIR' CONFIG_ETRAX_DEF_R_PORT_PA_DIR 1c @@ -132,13 +143,13 @@ bool 'Software Shutdown Support' CONFIG_ETRAX_SOFT_SHUTDOWN if [ "$CONFIG_ETRAX_SOFT_SHUTDOWN" = "y" ]; then - int 'Shutdown bit on port CSP0' CONFIG_ETRAX_SHUTDOWN_BIT 12 - int 'Power button bit on port G' CONFIG_ETRAX_POWERBUTTON_BIT 25 + int ' Shutdown bit on port CSP0' CONFIG_ETRAX_SHUTDOWN_BIT 12 + int ' Power button bit on port G' CONFIG_ETRAX_POWERBUTTON_BIT 25 fi endmenu -# bring in Etrax built-in drivers +# bring in ETRAX built-in drivers source arch/cris/drivers/Config.in @@ -258,7 +269,7 @@ #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then - int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 + int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi source crypto/Config.in diff -urN linux-2.4.21-bk5/arch/cris/defconfig linux-2.4.21-bk6/arch/cris/defconfig --- linux-2.4.21-bk5/arch/cris/defconfig 2002-08-02 17:39:42.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/defconfig 2003-07-10 02:54:58.000000000 -0700 @@ -85,8 +85,12 @@ # CONFIG_ETRAX_ETHERNET_LPSLAVE is not set CONFIG_ETRAX_SERIAL=y CONFIG_ETRAX_SERIAL_PORT0=y +CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT=y +CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN=y # CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB is not set CONFIG_ETRAX_SERIAL_PORT1=y +CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT=y +CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN=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 diff -urN linux-2.4.21-bk5/arch/cris/drivers/Config.in linux-2.4.21-bk6/arch/cris/drivers/Config.in --- linux-2.4.21-bk5/arch/cris/drivers/Config.in 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/Config.in 2003-07-10 02:54:58.000000000 -0700 @@ -23,15 +23,21 @@ bool 'Serial-port support' CONFIG_ETRAX_SERIAL if [ "$CONFIG_ETRAX_SERIAL" = "y" ]; then - bool ' Use fast timers for serial DMA flush (experimental)' CONFIG_ETRAX_SERIAL_FAST_TIMER + bool ' Use fast timers for serial DMA flush' CONFIG_ETRAX_SERIAL_FAST_TIMER if [ "$CONFIG_ETRAX_SERIAL_FAST_TIMER" = "n" ]; then bool ' Fast serial port DMA flush' CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST if [ "$CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST" = "n" ]; then int ' Receive flush timeout (ticks) ' CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5 fi fi + bool ' Enable external clock on PB6' CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED + if [ "$CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED" = "y" ]; then + int ' Extern clock frequency (baudrate=clk/8) (Hz)' CONFIG_ETRAX_EXTERN_PB6CLK_FREQ + fi bool ' Serial port 0 enabled' CONFIG_ETRAX_SERIAL_PORT0 if [ "$CONFIG_ETRAX_SERIAL_PORT0" = "y" ]; then + bool ' Serial port 0 uses DMA6 out' CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT + bool ' Serial port 0 uses DMA7 in' CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN choice 'Ser0 DTR, RI, DSR and CD assignment' \ "No_DTR_RI_DSR_CD CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE \ DTR_RI_DSR_CD_on_PA CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PA \ @@ -67,6 +73,8 @@ bool ' Serial port 1 enabled' CONFIG_ETRAX_SERIAL_PORT1 if [ "$CONFIG_ETRAX_SERIAL_PORT1" = "y" ]; then + bool ' Serial port 1 uses DMA8 out' CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT + bool ' Serial port 1 uses DMA9 in' CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN choice 'Ser1 DTR, RI, DSR and CD assignment' \ "No_DTR_RI_DSR_CD CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_NONE \ DTR_RI_DSR_CD_on_PA CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PA \ @@ -106,6 +114,8 @@ fi bool ' Serial port 2 enabled' CONFIG_ETRAX_SERIAL_PORT2 if [ "$CONFIG_ETRAX_SERIAL_PORT2" = "y" ]; then + bool ' Serial port 2 uses DMA2 out' CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT + bool ' Serial port 2 uses DMA3 in' CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN choice 'Ser2 DTR, RI, DSR and CD assignment' \ "No_DTR_RI_DSR_CD CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE \ DTR_RI_DSR_CD_on_PA CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA \ @@ -139,6 +149,8 @@ fi bool ' Serial port 3 enabled' CONFIG_ETRAX_SERIAL_PORT3 if [ "$CONFIG_ETRAX_SERIAL_PORT3" = "y" ]; then + bool ' Serial port 3 uses DMA4 out' CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT + bool ' Serial port 3 uses DMA5 in' CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN choice 'Ser3 DTR, RI, DSR and CD assignment' \ "No_DTR_RI_DSR_CD CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_ON_NONE \ DTR_RI_DSR_CD_on_PA CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_ON_PA \ @@ -303,6 +315,7 @@ bool 'Real Time Clock support' CONFIG_ETRAX_RTC if [ "$CONFIG_ETRAX_RTC" = "y" ]; then + bool ' Enable RTC read only mode' CONFIG_ETRAX_RTC_READONLY choice ' RTC chip' \ "DS1302 CONFIG_ETRAX_DS1302 \ PCF8563 CONFIG_ETRAX_PCF8563" DS1302 diff -urN linux-2.4.21-bk5/arch/cris/drivers/axisflashmap.c linux-2.4.21-bk6/arch/cris/drivers/axisflashmap.c --- linux-2.4.21-bk5/arch/cris/drivers/axisflashmap.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/axisflashmap.c 2003-07-10 02:54:58.000000000 -0700 @@ -11,6 +11,9 @@ * partition split defined below. * * $Log: axisflashmap.c,v $ + * Revision 1.29 2003/04/01 14:12:06 starvik + * Added loglevel for lots of printks + * * Revision 1.28 2002/10/01 08:08:43 jonashg * The first partition ends at the start of the partition table. * @@ -300,7 +303,8 @@ { struct mtd_info *mtd_cs = NULL; - printk("%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n", + 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 @@ -387,7 +391,7 @@ struct partitiontable_head *ptable_head; 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"; + const char *pmsg = KERN_INFO " /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 @@ -396,7 +400,7 @@ panic("axisflashmap found no flash chip!\n"); } - printk("%s: 0x%08x bytes of flash memory.\n", + printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n", mymtd->name, mymtd->size); mymtd->module = THIS_MODULE; @@ -440,7 +444,7 @@ ptable_ok = (csum == ptable_head->checksum); /* Read the entries and use/show the info. */ - printk(" Found a%s partition table at 0x%p-0x%p.\n", + printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n", (ptable_ok ? " valid" : "n invalid"), ptable_head, max_addr); @@ -472,14 +476,14 @@ axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; axis_partitions[pidx].mask_flags |= MTD_WRITEABLE; - printk(" Adding readonly flash partition for romfs image:\n"); + printk(KERN_INFO " Adding readonly flash partition for romfs image:\n"); printk(pmsg, pidx, axis_partitions[pidx].offset, axis_partitions[pidx].size); pidx++; } if (use_default_ptable) { - printk(" Using default partition table.\n"); + printk(KERN_INFO " Using default partition table.\n"); err = add_mtd_partitions(mymtd, axis_default_partitions, NUM_DEFAULT_PARTITIONS); } else { @@ -508,7 +512,7 @@ "mtd_info!\n"); } - printk(" Adding RAM partition for romfs image:\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, diff -urN linux-2.4.21-bk5/arch/cris/drivers/ds1302.c linux-2.4.21-bk6/arch/cris/drivers/ds1302.c --- linux-2.4.21-bk5/arch/cris/drivers/ds1302.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/ds1302.c 2003-07-10 02:54:58.000000000 -0700 @@ -4,9 +4,22 @@ *! *! DESCRIPTION: Implements an interface for the DS1302 RTC through Etrax I/O *! -*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status +*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init *! *! $Log: ds1302.c,v $ +*! Revision 1.19 2003/06/12 08:02:05 johana +*! Removed faulty comma from printk. +*! Fixed warning () -> (void) +*! +*! Revision 1.18 2003/04/16 09:02:28 oskarp +*! * Merged change_branch--rtc_readonly to main. +*! +*! Revision 1.17 2003/04/01 14:12:06 starvik +*! Added loglevel for lots of printks +*! +*! Revision 1.16 2003/02/19 15:51:16 cedric +*! Start the clock from ds1302_init() +*! *! Revision 1.15 2002/10/11 16:14:33 johana *! Added CONFIG_ETRAX_DS1302_TRICKLE_CHARGE and initial setting of the *! trcklecharge register. @@ -95,7 +108,7 @@ *! *! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN *! -*! $Id: ds1302.c,v 1.15 2002/10/11 16:14:33 johana Exp $ +*! $Id: ds1302.c,v 1.19 2003/06/12 08:02:05 johana Exp $ *! *!***************************************************************************/ @@ -263,12 +276,23 @@ void ds1302_writereg(int reg, unsigned char val) { - ds1302_wenable(); - start(); - out_byte(0x80 | (reg << 1)); /* write register */ - out_byte(val); - stop(); - ds1302_wdisable(); +#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 @@ -405,20 +429,33 @@ 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; } } -int -get_rtc_status(char *buf) +static void +print_rtc_status(void) { - char *p; struct rtc_time tm; - p = buf; - get_rtc_time(&tm); /* @@ -426,13 +463,10 @@ * time or for Universal Standard Time (GMT). Probably local though. */ - p += sprintf(p, - "rtc_time\t: %02d:%02d:%02d\n" - "rtc_date\t: %04d-%02d-%02d\n", - tm.tm_hour, tm.tm_min, tm.tm_sec, - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); - - return p - buf; + 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); } @@ -466,11 +500,10 @@ out_byte(0xc1); /* read RAM byte 0 */ if((res = in_byte()) == MAGIC_PATTERN) { - char buf[100]; stop(); ds1302_wdisable(); - printk("%s: RTC found.\n", ds1302_name); - printk("%s: SDA, SCL, RST on PB%i, PB%i, %s%i\n", + 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, @@ -480,12 +513,10 @@ "PB", #endif CONFIG_ETRAX_DS1302_RSTBIT); - get_rtc_status(buf); - printk(buf); + print_rtc_status(); retval = 1; } else { stop(); - printk("%s: RTC not found.\n", ds1302_name); retval = 0; } @@ -518,9 +549,12 @@ (IO_STATE(R_GEN_CONFIG, g0dir, out))); *R_GEN_CONFIG = genconfig_shadow; #endif - if (!ds1302_probe()) + 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 } @@ -533,5 +567,7 @@ /* 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; } diff -urN linux-2.4.21-bk5/arch/cris/drivers/eeprom.c linux-2.4.21-bk6/arch/cris/drivers/eeprom.c --- linux-2.4.21-bk5/arch/cris/drivers/eeprom.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/eeprom.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,4 +1,4 @@ -/*!***************************************************************************** +/****************************************************************************** *! *! Implements an interface for i2c compatible eeproms to run under linux. *! Supports 2k, 8k(?) and 16k. Uses adaptive timing adjustents by @@ -20,6 +20,18 @@ *! in the spin-lock. *! *! $Log: eeprom.c,v $ +*! Revision 1.12 2003/04/09 08:31:14 pkj +*! Typo correction (taken from Linux 2.5). +*! +*! Revision 1.11 2003/02/12 20:43:46 johana +*! Previous checkin removed beginning of comment. +*! +*! Revision 1.10 2003/02/10 07:18:20 starvik +*! Removed misplaced ; +*! +*! Revision 1.9 2003/01/22 06:54:46 starvik +*! Fixed warnings issued by GCC 3.2.1 +*! *! Revision 1.8 2001/06/15 13:24:29 jonashg *! * Added verification of pointers from userspace in read and write. *! * Made busy counter volatile. @@ -203,7 +215,7 @@ * 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 diferent value to one of the locations, + * 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. */ @@ -493,7 +505,7 @@ static ssize_t eeprom_read(struct file * file, char * buf, size_t count, loff_t *off) { - int i, read=0; + int read=0; unsigned long p = file->f_pos; unsigned char page; @@ -519,7 +531,7 @@ if(!eeprom_address(p)) { printk(KERN_INFO "%s: Read failed to address the eeprom: " - "0x%08X (%i) page: %i\n", eeprom_name, p, p, page); + "0x%08lX (%li) page: %i\n", eeprom_name, p, p, page); i2c_stop(); /* don't forget to wake them up */ @@ -602,7 +614,7 @@ if(!eeprom_address(p)) { printk(KERN_INFO "%s: Write failed to address the eeprom: " - "0x%08X (%i) \n", eeprom_name, p, p); + "0x%08lX (%li) \n", eeprom_name, p, p); i2c_stop(); /* don't forget to wake them up */ @@ -740,7 +752,7 @@ static int eeprom_address(unsigned long addr) { - int i, j; + int i; unsigned char page, offset; page = (unsigned char) (addr >> 8); @@ -795,7 +807,7 @@ return 1; } -/* Reads from current adress. */ +/* Reads from current address. */ static int read_from_eeprom(char * buf, int count) { diff -urN linux-2.4.21-bk5/arch/cris/drivers/ethernet.c linux-2.4.21-bk6/arch/cris/drivers/ethernet.c --- linux-2.4.21-bk5/arch/cris/drivers/ethernet.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/ethernet.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: ethernet.c,v 1.34 2002/12/13 07:17:44 starvik Exp $ +/* $Id: ethernet.c,v 1.44 2003/07/01 10:55:07 starvik Exp $ * * e100net.c: A network driver for the ETRAX 100LX network controller. * @@ -7,9 +7,52 @@ * The outline of this driver comes from skeleton.c. * * $Log: ethernet.c,v $ - * Revision 1.34 2002/12/13 07:17:44 starvik - * Basic ethtool and MII ioctls - * Handle out of memory when allocating new buffers + * Revision 1.44 2003/07/01 10:55:07 starvik + * Never bring down link to make stupid POE equipment happy + * + * Revision 1.43 2003/04/24 08:26:50 starvik + * New LED behaviour: LED off when no link + * + * Revision 1.42 2003/04/10 07:14:58 starvik + * Merge of changes from todo list + * + * Revision 1.41 2003/04/09 08:31:14 pkj + * Typo correction (taken from Linux 2.5). + * + * Revision 1.40 2003/04/01 14:12:06 starvik + * Added loglevel for lots of printks + * + * Revision 1.39.2.3 2003/04/01 07:51:14 starvik + * Default Ethernet Stations Address can be specified with command line option. + * + * Revision 1.39.2.2 2003/03/31 15:41:07 starvik + * Only communicate with transciever when ETRAX is properly configured. + * + * Revision 1.39.2.1 2003/03/31 14:12:46 starvik + * Transmit interrupt always enabled. This has two side effects: + * 1. UDP (and possibly other protocols) works on quiet networks + * 2. Slightly lower transmit performance + * + * Revision 1.39 2003/03/06 15:45:39 henriken + * Off by one error in group address register setting. + * + * Revision 1.38 2003/02/27 17:23:07 starvik + * Corrected Rev to Revision + * + * Revision 1.37 2003/02/27 10:52:59 magnusmn + * More generic transceiver support. + * + * Revision 1.33.2.4 2003/02/24 16:51:26 magnusmn + * TDK specific registers to check speed/duplex + * + * Revision 1.33.2.3 2003/02/24 09:16:31 magnusmn + * ethernet.c + * + * Revision 1.33.2.2 2003/02/21 11:57:27 magnusmn + * Merging differences between 1.36 and 1.33 into ethernet.c (needed ethtool ioctl) + * + * Revision 1.33.2.1 2002/12/04 07:54:49 starvik + * First attempt to get more generic transceiver support * * Revision 1.33 2002/10/02 20:16:17 hp * SETF, SETS: Use underscored IO_x_ macros rather than incorrect token concatenation @@ -220,6 +263,16 @@ struct sk_buff* skb; } etrax_eth_descr; +/* Some transceivers requires special handling */ +struct transceiver_ops +{ + unsigned int oui; + void (*check_speed)(void); + void (*check_duplex)(void); +}; + +struct transceiver_ops* transceiver; + /* Duplex settings */ enum duplex { @@ -240,10 +293,17 @@ */ #define MDIO_BASE_STATUS_REG 0x1 #define MDIO_BASE_CONTROL_REG 0x0 +#define MDIO_PHY_ID_HIGH_REG 0x2 +#define MDIO_PHY_ID_LOW_REG 0x3 #define MDIO_BC_NEGOTIATE 0x0200 #define MDIO_BC_FULL_DUPLEX_MASK 0x0100 #define MDIO_BC_AUTO_NEG_MASK 0x1000 #define MDIO_BC_SPEED_SELECT_MASK 0x2000 +#define MDIO_STATUS_100_FD 0x4000 +#define MDIO_STATUS_100_HD 0x2000 +#define MDIO_STATUS_10_FD 0x1000 +#define MDIO_STATUS_10_HD 0x0800 +#define MDIO_STATUS_SPEED_DUPLEX_MASK 0x7800 #define MDIO_ADVERTISMENT_REG 0x4 #define MDIO_ADVERT_100_FD 0x100 #define MDIO_ADVERT_100_HD 0x080 @@ -257,9 +317,13 @@ /* Broadcom specific */ #define MDIO_AUX_CTRL_STATUS_REG 0x18 -#define MDIO_FULL_DUPLEX_IND 0x1 -#define MDIO_SPEED 0x2 -#define MDIO_PHYS_ADDR 0x0 +#define MDIO_BC_FULL_DUPLEX_IND 0x1 +#define MDIO_BC_SPEED 0x2 + +/* TDK specific */ +#define MDIO_TDK_DIAGNOSTIC_REG 18 +#define MDIO_TDK_DIAGNOSTIC_RATE 0x400 +#define MDIO_TDK_DIAGNOSTIC_DPLX 0x800 /* Network flash constants */ #define NET_FLASH_TIME (HZ/50) /* 20 ms */ @@ -303,11 +367,12 @@ static etrax_eth_descr TxDescList[NBR_OF_TX_DESC] __attribute__ ((aligned(32))); static unsigned int network_rec_config_shadow = 0; +static unsigned int mdio_phy_addr; /* Transciever address */ /* Network speed indication. */ static struct timer_list speed_timer; static struct timer_list clear_led_timer; -static int current_speed; /* Speed read from tranceiver */ +static int current_speed; /* Speed read from transceiver */ static int current_speed_selection; /* Speed selected by user */ static int led_next_time; static int led_active; @@ -337,6 +402,7 @@ static void e100_hardware_send_packet(char *buf, int length); static void update_rx_stats(struct net_device_stats *); static void update_tx_stats(struct net_device_stats *); +static int e100_probe_transceiver(void); static void e100_check_speed(unsigned long dummy); static void e100_set_speed(unsigned long speed); @@ -349,11 +415,25 @@ static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd); static void e100_send_mdio_bit(unsigned char bit); static unsigned char e100_receive_mdio_bit(void); -static void e100_reset_tranceiver(void); +static void e100_reset_transceiver(void); static void e100_clear_network_leds(unsigned long dummy); static void e100_set_network_leds(int active); +static void broadcom_check_speed(void); +static void broadcom_check_duplex(void); +static void tdk_check_speed(void); +static void tdk_check_duplex(void); +static void generic_check_speed(void); +static void generic_check_duplex(void); + +struct transceiver_ops transceivers[] = +{ + {0x1018, broadcom_check_speed, broadcom_check_duplex}, /* Broadcom */ + {0xC039, tdk_check_speed, tdk_check_duplex}, /* TDK */ + {0x0000, generic_check_speed, generic_check_duplex} /* Generic, must be last */ +}; + #define tx_done(dev) (*R_DMA_CH0_CMD == 0) /* @@ -369,11 +449,12 @@ { int i; - printk("ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2001 Axis Communications AB\n"); + printk(KERN_INFO + "ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2001 Axis Communications AB\n"); dev->base_addr = (unsigned int)R_NETWORK_SA_0; /* just to have something to show */ - printk("%s initialized\n", dev->name); + printk(KERN_INFO "%s initialized\n", dev->name); /* make Linux aware of the new hardware */ @@ -468,7 +549,6 @@ current_speed_selection = 0; /* Auto */ speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL; speed_timer.function = e100_check_speed; - add_timer(&speed_timer); clear_led_timer.function = e100_clear_network_leds; @@ -476,8 +556,11 @@ current_duplex = autoneg; duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL; duplex_timer.function = e100_check_duplex; - add_timer(&duplex_timer); + /* Initialize group address registers to make sure that no */ + /* unwanted addresses are matched */ + *R_NETWORK_GA_0 = 0x00000000; + *R_NETWORK_GA_1 = 0x00000000; return 0; } @@ -508,8 +591,7 @@ /* show it in the log as well */ - printk("%s: changed MAC to ", dev->name); - + printk(KERN_INFO "%s: changed MAC to ", dev->name); for (i = 0; i < 5; i++) printk("%02X:", dev->dev_addr[i]); @@ -532,12 +614,6 @@ { unsigned long flags; - /* disable the ethernet interface while we configure it */ - - *R_NETWORK_GEN_CONFIG = - IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) | - IO_STATE(R_NETWORK_GEN_CONFIG, enable, off); - /* enable the MDIO output pin */ *R_NETWORK_MGM_CTRL = IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable); @@ -636,7 +712,8 @@ /* enable the irq's for ethernet DMA */ *R_IRQ_MASK2_SET = - IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set); + IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) | + IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set); *R_IRQ_MASK0_SET = IO_STATE(R_IRQ_MASK0_SET, overrun, set) | @@ -665,6 +742,14 @@ restore_flags(flags); + /* Probe for transceiver */ + if (e100_probe_transceiver()) + goto grace_exit4; + + /* Start duplex/speed timers */ + add_timer(&speed_timer); + add_timer(&duplex_timer); + /* We are now ready to accept transmit requeusts from * the queueing layer of the networking. */ @@ -684,6 +769,33 @@ return -EAGAIN; } +static void +generic_check_speed(void) +{ + unsigned long data; + data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG); + if ((data & MDIO_ADVERT_100_FD) || + (data & MDIO_ADVERT_100_HD)) + current_speed = 100; + else + current_speed = 10; +} + +static void +tdk_check_speed(void) +{ + unsigned long data; + data = e100_get_mdio_reg(MDIO_TDK_DIAGNOSTIC_REG); + current_speed = (data & MDIO_TDK_DIAGNOSTIC_RATE ? 100 : 10); +} + +static void +broadcom_check_speed(void) +{ + unsigned long data; + data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG); + current_speed = (data & MDIO_BC_SPEED ? 100 : 10); +} static void e100_check_speed(unsigned long dummy) @@ -695,8 +807,7 @@ if (!(data & MDIO_LINK_UP_MASK)) { current_speed = 0; } else { - data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG); - current_speed = (data & MDIO_SPEED ? 100 : 10); + transceiver->check_speed(); } if (old_speed != current_speed) @@ -761,29 +872,21 @@ static void e100_set_speed(unsigned long speed) { - current_speed_selection = speed; - e100_negotiate(); + if (speed != current_speed_selection) { + current_speed_selection = speed; + e100_negotiate(); + } } static void e100_check_duplex(unsigned long dummy) { - unsigned long data; - - data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG); - - if (data & MDIO_FULL_DUPLEX_IND) { - if (!full_duplex) { /* Duplex changed to full? */ - full_duplex = 1; - SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex); - *R_NETWORK_REC_CONFIG = network_rec_config_shadow; - } - } else { /* half */ - if (full_duplex) { /* Duplex changed to half? */ - full_duplex = 0; - SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex); - *R_NETWORK_REC_CONFIG = network_rec_config_shadow; - } + int old_duplex = full_duplex; + transceiver->check_duplex(); + if (old_duplex != full_duplex) { + /* Duplex changed */ + SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex); + *R_NETWORK_REC_CONFIG = network_rec_config_shadow; } /* Reinitialize the timer. */ @@ -791,13 +894,71 @@ add_timer(&duplex_timer); } +static void +generic_check_duplex(void) +{ + unsigned long data; + data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG); + if ((data & MDIO_ADVERT_100_FD) || + (data & MDIO_ADVERT_10_FD)) + full_duplex = 1; + else + full_duplex = 0; +} + +static void +tdk_check_duplex(void) +{ + unsigned long data; + data = e100_get_mdio_reg(MDIO_TDK_DIAGNOSTIC_REG); + full_duplex = (data & MDIO_TDK_DIAGNOSTIC_DPLX) ? 1 : 0; +} + +static void +broadcom_check_duplex(void) +{ + unsigned long data; + data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG); + full_duplex = (data & MDIO_BC_FULL_DUPLEX_IND) ? 1 : 0; +} + static void e100_set_duplex(enum duplex new_duplex) { - current_duplex = new_duplex; - e100_negotiate(); + if (new_duplex != current_duplex) { + current_duplex = new_duplex; + e100_negotiate(); + } } +static int +e100_probe_transceiver(void) +{ + unsigned int phyid_high; + unsigned int phyid_low; + unsigned int oui; + struct transceiver_ops* ops = NULL; + + /* Probe MDIO physical address */ + for (mdio_phy_addr = 0; mdio_phy_addr <= 31; mdio_phy_addr++) { + if (e100_get_mdio_reg(MDIO_BASE_STATUS_REG) != 0xffff) + break; + } + if (mdio_phy_addr == 32) + return -ENODEV; + + /* Get manufacturer */ + phyid_high = e100_get_mdio_reg(MDIO_PHY_ID_HIGH_REG); + phyid_low = e100_get_mdio_reg(MDIO_PHY_ID_LOW_REG); + oui = (phyid_high << 6) | (phyid_low >> 10); + + for (ops = &transceivers[0]; ops->oui; ops++) { + if (ops->oui == oui) + break; + } + transceiver = ops; + return 0; +} static unsigned short e100_get_mdio_reg(unsigned char reg_num) @@ -807,7 +968,7 @@ int bitCounter; /* Start of frame, OP Code, Physical Address, Register Address */ - cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (MDIO_PHYS_ADDR << 7) | + cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (mdio_phy_addr << 7) | (reg_num << 2); e100_send_mdio_cmd(cmd, 0); @@ -828,7 +989,7 @@ int bitCounter; unsigned short cmd; - cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) | + cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) | (reg << 2); e100_send_mdio_cmd(cmd, 1); @@ -888,7 +1049,7 @@ } static void -e100_reset_tranceiver(void) +e100_reset_transceiver(void) { unsigned short cmd; unsigned short data; @@ -896,7 +1057,7 @@ data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG); - cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) | (MDIO_BASE_CONTROL_REG << 2); + cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) | (MDIO_BASE_CONTROL_REG << 2); e100_send_mdio_cmd(cmd, 1); @@ -928,9 +1089,9 @@ RESET_DMA(NETWORK_TX_DMA_NBR); WAIT_DMA(NETWORK_TX_DMA_NBR); - /* Reset the tranceiver. */ + /* Reset the transceiver. */ - e100_reset_tranceiver(); + e100_reset_transceiver(); /* and get rid of the packets that never got an interrupt */ while (myFirstTxDesc != myNextTxDesc) @@ -978,9 +1139,6 @@ /* Stop queue if full */ if (myNextTxDesc == myFirstTxDesc) { - /* Enable transmit interrupt to wake up queue */ - *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do); - *R_IRQ_MASK2_SET = IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set); netif_stop_queue(dev); } @@ -1001,6 +1159,11 @@ struct net_local *np = (struct net_local *)dev->priv; unsigned long irqbits = *R_IRQ_MASK2_RD; + /* Disable RX/TX IRQs to avoid reentrancy */ + *R_IRQ_MASK2_CLR = + IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr) | + IO_STATE(R_IRQ_MASK2_CLR, dma1_eop, clr); + /* Handle received packets */ if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) { /* acknowledge the eop interrupt */ @@ -1044,9 +1207,13 @@ if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) { /* acknowledge the eop interrupt and wake up queue */ *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do); - *R_IRQ_MASK2_CLR = IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr); netif_wake_queue(dev); } + + /* Enable RX/TX IRQs again */ + *R_IRQ_MASK2_SET = + IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) | + IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set); } static void @@ -1183,14 +1350,10 @@ { struct net_local *np = (struct net_local *)dev->priv; - printk("Closing %s.\n", dev->name); + printk(KERN_INFO "Closing %s.\n", dev->name); netif_stop_queue(dev); - *R_NETWORK_GEN_CONFIG = - IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) | - IO_STATE(R_NETWORK_GEN_CONFIG, enable, off); - *R_IRQ_MASK0_CLR = IO_STATE(R_IRQ_MASK0_CLR, overrun, clr) | IO_STATE(R_IRQ_MASK0_CLR, underrun, clr) | @@ -1221,6 +1384,9 @@ update_rx_stats(&np->stats); update_tx_stats(&np->stats); + /* Stop speed/duplex timers */ + del_timer(&speed_timer); + del_timer(&duplex_timer); return 0; } @@ -1233,7 +1399,7 @@ case SIOCETHTOOL: return e100_ethtool_ioctl(dev,ifr); case SIOCGMIIPHY: /* Get PHY address */ - data->phy_id = MDIO_PHYS_ADDR; + data->phy_id = mdio_phy_addr; break; case SIOCGMIIREG: /* Read MII register */ data->val_out = e100_get_mdio_reg(data->reg_num); @@ -1252,7 +1418,7 @@ case SET_ETH_SPEED_AUTO: /* Auto negotiate speed */ e100_set_speed(0); break; - case SET_ETH_DUPLEX_HALF: /* Hhalf duplex. */ + case SET_ETH_DUPLEX_HALF: /* Half duplex. */ e100_set_duplex(half); break; case SET_ETH_DUPLEX_FULL: /* Full duplex. */ @@ -1285,7 +1451,7 @@ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; ecmd.port = PORT_TP; ecmd.transceiver = XCVR_EXTERNAL; - ecmd.phy_address = MDIO_PHYS_ADDR; + ecmd.phy_address = mdio_phy_addr; ecmd.speed = current_speed; ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF; ecmd.advertising = ADVERTISED_TP; @@ -1328,7 +1494,7 @@ struct ethtool_drvinfo info; memset((void *) &info, 0, sizeof (info)); strncpy(info.driver, "ETRAX 100LX", sizeof(info.driver) - 1); - strncpy(info.version, "$Rev$", sizeof(info.version) - 1); + strncpy(info.version, "$Revision: 1.44 $", sizeof(info.version) - 1); strncpy(info.fw_version, "N/A", sizeof(info.fw_version) - 1); strncpy(info.bus_info, "N/A", sizeof(info.bus_info) - 1); info.regdump_len = 0; @@ -1457,7 +1623,7 @@ hash_ix &= 0x3f; - if (hash_ix > 32) { + if (hash_ix >= 32) { hi_bits |= (1 << (hash_ix-32)); } else { @@ -1526,7 +1692,7 @@ if (!current_speed) { /* Make LED red, link is down */ - LED_NETWORK_SET(LED_RED); + LED_NETWORK_SET(LED_OFF); } else if (light_leds) { if (current_speed == 10) { @@ -1555,4 +1721,26 @@ return -ENODEV; } +static int __init +e100_boot_setup(char* str) +{ + struct sockaddr sa = {0}; + int i; + + /* Parse the colon separated Ethernet station address */ + for (i = 0; i < ETH_ALEN; i++) { + unsigned int tmp; + if (sscanf(str + 3*i, "%2x", &tmp) != 1) { + printk(KERN_WARNING "Malformed station address"); + return 0; + } + sa.sa_data[i] = (char)tmp; + } + + default_mac = sa; + return 1; +} + +__setup("etrax100_eth=", e100_boot_setup); + module_init(etrax_init_module); diff -urN linux-2.4.21-bk5/arch/cris/drivers/examples/kiobuftest.c linux-2.4.21-bk6/arch/cris/drivers/examples/kiobuftest.c --- linux-2.4.21-bk5/arch/cris/drivers/examples/kiobuftest.c 2001-04-06 10:42:55.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/examples/kiobuftest.c 2003-07-10 02:54:58.000000000 -0700 @@ -4,18 +4,12 @@ * * It is necessary because the pages the virtual pointers reference, might * not exist in memory (could be mapped to the zero-page, filemapped etc) - * and DMA cannot trigger the MMU to force them in (and would have time + * and DMA cannot trigger the MMU to force them in (and would have time * contraints making it impossible to wait for it anyway). * - * Author: Bjorn Wesen - * - * $Log: kiobuftest.c,v $ - * Revision 1.2 2001/02/27 13:52:50 bjornw - * malloc.h -> slab.h - * - * Revision 1.1 2001/01/19 15:57:49 bjornw - * Example of how to do direct HW -> user-mode DMA + * Copyright (c) 2001, 2002, 2003 Axis Communications AB * + * Author: Bjorn Wesen * */ @@ -32,55 +26,55 @@ #define KIOBUFTEST_MAJOR 124 /* in the local range, experimental */ - static ssize_t kiobuf_read(struct file *filp, char *buf, size_t len, loff_t *ppos) { + struct kiobuf *iobuf; + int res, i; - struct kiobuf *iobuf; - int res, i; - - /* Make a kiobuf that maps the entire length the reader has given - * us - */ - - res = alloc_kiovec(1, &iobuf); - if (res) - return res; - - if((res = map_user_kiobuf(READ, iobuf, (unsigned long)buf, len))) { - printk("map_user_kiobuf failed, return %d\n", res); - return res; - } - - /* At this point, the virtual area buf[0] -> buf[len-1] will - * have corresponding pages mapped in physical memory and locked - * until we unmap the kiobuf. They cannot be swapped out or moved - * around. - */ - - printk("nr_pages == %d\noffset == %d\nlength == %d\n", - iobuf->nr_pages, iobuf->offset, iobuf->length); - - for(i = 0; i < iobuf->nr_pages; i++) { - printk("page_add(maplist[%d]) == 0x%x\n", i, - page_address(iobuf->maplist[i])); - } - - /* This is the place to create the necessary scatter-gather vector - * for the DMA using the iobuf->maplist array and page_address - * (don't forget __pa if the DMA needs the actual physical DRAM address) - * and run it. - */ + /* + * Make a kiobuf that maps the entire length the reader has given us. + */ + res = alloc_kiovec(1, &iobuf); + if (res) + return res; + + if ((res = map_user_kiobuf(READ, iobuf, (unsigned long)buf, len))) { + printk("map_user_kiobuf failed, return %d\n", res); + free_kiovec(1, &iobuf); + return res; + } + + /* + * At this point, the virtual area buf[0] -> buf[len-1] will have + * corresponding pages mapped in physical memory and locked until + * we unmap the kiobuf. They cannot be swapped out or moved around. + */ + + printk("nr_pages == %d\noffset == %d\nlength == %d\n", + iobuf->nr_pages, iobuf->offset, iobuf->length); + + for (i = 0; i < iobuf->nr_pages; i++) { + printk("page_add(maplist[%d]) == 0x%x\n", i, + page_address(iobuf->maplist[i])); + } + + /* + * This is the place to create the necessary scatter-gather vector + * for the DMA using the iobuf->maplist array and page_address (don't + * forget __pa if the DMA needs the actual physical DRAM address) + * and run it. + */ - /* Release the mapping and exit */ + /* Release the mapping and exit */ - unmap_kiobuf(iobuf); /* The unlock_kiobuf is implicit here */ + unmap_kiobuf(iobuf); /* The unlock_kiobuf is implicit here */ + free_kiovec(1, &iobuf); - return len; + return len; } @@ -97,7 +91,7 @@ /* register char device */ res = register_chrdev(KIOBUFTEST_MAJOR, "kiobuftest", &kiobuf_fops); - if(res < 0) { + if (res < 0) { printk(KERN_ERR "kiobuftest: couldn't get a major number.\n"); return res; } @@ -105,4 +99,11 @@ printk("Initializing kiobuf-test device\n"); } +static void __exit +kiobuftest_exit(void) +{ + unregister_chrdev(KIOBUFTEST_MAJOR, "kiobuftest"); +} + module_init(kiobuftest_init); +module_exit(kiobuftest_exit); diff -urN linux-2.4.21-bk5/arch/cris/drivers/gpio.c linux-2.4.21-bk6/arch/cris/drivers/gpio.c --- linux-2.4.21-bk5/arch/cris/drivers/gpio.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/gpio.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: gpio.c,v 1.21 2002/12/02 08:11:41 starvik Exp $ +/* $Id: gpio.c,v 1.25 2003/06/24 07:24:31 johana Exp $ * * Etrax general port I/O device * @@ -9,6 +9,22 @@ * Johan Adolfsson (read/set directions, write, port G) * * $Log: gpio.c,v $ + * Revision 1.25 2003/06/24 07:24:31 johana + * Added gpio_pa_irq_enabled_mask to keep track of what interrupts we have + * enabled so we anly clear those that we have enabled and let bits/pins + * used by other drivers alone. + * Update gpio_some_alarms in IO_CLRALARM ioctl(). + * + * Revision 1.24 2003/06/19 08:58:25 johana + * Turn off interrupts while fiddling with genconfig_shadow. + * Put some printk within D() macro. + * + * Revision 1.23 2003/04/01 14:12:06 starvik + * Added loglevel for lots of printks + * + * Revision 1.22 2003/01/09 18:03:47 starvik + * init_ioremap is now called by kernel before device drivers are initialized + * * Revision 1.21 2002/12/02 08:11:41 starvik * Merge of Linux 2.4.20 * @@ -169,6 +185,7 @@ 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) @@ -238,13 +255,19 @@ 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; - *R_IRQ_MASK1_SET = (tmp << R_IRQ_MASK1_SET__pa0__BITNR); + 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) @@ -293,12 +316,15 @@ { unsigned long tmp; /* Find what PA interrupts are active */ - tmp = (*R_IRQ_READ1 >> R_IRQ_READ1__pa0__BITNR) & 0xFF; + tmp = (*R_IRQ_READ1); + + /* Find those that we have enabled */ + tmp &= gpio_pa_irq_enabled_mask; + /* Clear them.. */ - /* NOTE: Maybe we need to be more careful here if some other - * driver uses PA interrupt as well? - */ - *R_IRQ_MASK1_CLR = (tmp << R_IRQ_MASK1_CLR__pa0__BITNR); + *R_IRQ_MASK1_CLR = tmp; + gpio_pa_irq_enabled_mask &= ~tmp; + if (gpio_some_alarms) { etrax_gpio_wake_up_check(); } @@ -437,7 +463,7 @@ p = p->next; } gpio_some_alarms = 0; - + return 0; } @@ -459,6 +485,7 @@ 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; @@ -483,16 +510,17 @@ dir_g_in_bits |= (1<<24); dir_g_out_bits &= ~(1<<24); } - printk("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); + 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; @@ -508,7 +536,8 @@ restore_flags(flags); return *priv->dir_shadow; } else if (priv->minor == GPIO_MINOR_G) { - /* We must fiddle with R_GEN_CONFIG to change dir */ + /* 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 */ @@ -532,15 +561,16 @@ dir_g_out_bits |= (1<<24); dir_g_in_bits &= ~(1<<24); } - printk("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); + 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; @@ -605,6 +635,20 @@ // 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 */ @@ -824,9 +868,11 @@ dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g); - printk("GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n", + 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("GPIO port G: dir: %08lX changeable: %08lX\n", + printk(KERN_INFO + "GPIO port G: dir: %08lX changeable: %08lX\n", dir_g_shadow, changeable_dir_g); } @@ -835,7 +881,6 @@ static __init int gpio_init(void) { - extern void init_ioremap(void); int res; #if defined (CONFIG_ETRAX_CSP0_LEDS) int i; @@ -850,8 +895,7 @@ } /* Clear all leds */ -#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) - init_ioremap(); +#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); @@ -865,7 +909,8 @@ #endif gpio_init_port_g(); - printk("ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002 Axis Communications AB\n"); + 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 @@ -873,11 +918,11 @@ */ if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, SA_SHIRQ | SA_INTERRUPT,"gpio poll", NULL)) { - printk("err: timer0 irq for gpio\n"); + 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("err: PA irq for gpio\n"); + printk(KERN_CRIT "err: PA irq for gpio\n"); } return res; diff -urN linux-2.4.21-bk5/arch/cris/drivers/i2c.c linux-2.4.21-bk6/arch/cris/drivers/i2c.c --- linux-2.4.21-bk5/arch/cris/drivers/i2c.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/i2c.c 2003-07-10 02:54:58.000000000 -0700 @@ -11,7 +11,26 @@ *! Jan 14 2000 Johan Adolfsson Fixed PB shadow register stuff - *! don't use PB_I2C if DS1302 uses same bits, *! use PB. +*| June 23 2003 Pieter Grimmerink Added 'i2c_sendnack'. i2c_readreg now +*| generates nack on last received byte, +*| instead of ack. +*| i2c_getack changed data level while clock +*| was high, causing DS75 to see a stop condition +*| *! $Log: i2c.c,v $ +*! Revision 1.12 2003/06/23 14:43:47 oskarp +*! * i2c_sendnack is added. +*! - i2c_readreg now generates nack on last received byte, instead of ack. +*! i2c_getack changed data level while clock was high, causing DS75 to +*! see a stop condition. +*! +*! Revision 1.11 2003/05/23 09:37:23 oskarp +*! * Made i2c_init() visible. +*! - e.g. pcf856.c uses i2c_init(). +*! +*! Revision 1.10 2003/04/01 14:12:06 starvik +*! Added loglevel for lots of printks +*! *! 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 @@ -53,7 +72,7 @@ *! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: i2c.c,v 1.9 2002/10/31 15:32:26 starvik Exp $ */ +/* $Id: i2c.c,v 1.12 2003/06/23 14:43:47 oskarp Exp $ */ /****************** INCLUDE FILES SECTION ***********************************/ #include @@ -133,7 +152,7 @@ #define i2c_getbit() (((*R_PORT_PB_READ & (1 << SDABIT))) >> SDABIT) -#else +#else /* !CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C */ /* 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)) @@ -163,7 +182,8 @@ /* read a bit from the i2c interface */ #define i2c_getbit() (*R_PORT_PB_READ & 0x1) -#endif + +#endif /* CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C */ /* use the kernels delay routine */ @@ -299,6 +319,12 @@ } 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; } @@ -360,6 +386,13 @@ 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 */ @@ -417,6 +450,37 @@ /*#--------------------------------------------------------------------------- *# +*# 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 @@ -546,9 +610,10 @@ */ b = i2c_inbyte(); /* - * send Ack + * last received byte needs to be nacked + * instead of acked */ - i2c_sendack(); + i2c_sendnack(); /* * end sequence */ @@ -623,7 +688,7 @@ release: i2c_release, }; -static int __init +int __init i2c_init(void) { int res; @@ -653,7 +718,7 @@ return res; } - printk("I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n"); + printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n"); return 0; } diff -urN linux-2.4.21-bk5/arch/cris/drivers/i2c.h linux-2.4.21-bk6/arch/cris/drivers/i2c.h --- linux-2.4.21-bk5/arch/cris/drivers/i2c.h 2001-05-19 17:43:05.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/i2c.h 2003-07-10 02:54:58.000000000 -0700 @@ -1,6 +1,8 @@ -/* $Id: i2c.h,v 1.3 2001/03/19 12:43:01 markusl Exp $ */ + +#include /* High level I2C actions */ +int __init i2c_init(void); int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue); unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg); @@ -11,6 +13,3 @@ unsigned char i2c_inbyte(void); int i2c_getack(void); void i2c_sendack(void); - - - diff -urN linux-2.4.21-bk5/arch/cris/drivers/ide.c linux-2.4.21-bk6/arch/cris/drivers/ide.c --- linux-2.4.21-bk5/arch/cris/drivers/ide.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/ide.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: ide.c,v 1.26 2002/09/17 12:16:59 bjornw Exp $ +/* $Id: ide.c,v 1.30 2003/07/08 07:24:47 pkj Exp $ * * Etrax specific IDE functions, like init and PIO-mode setting etc. * Almost the entire ide.c is used for the rest of the Etrax ATA driver. @@ -8,6 +8,19 @@ * Mikael Starvik (pio setup stuff) * * $Log: ide.c,v $ + * Revision 1.30 2003/07/08 07:24:47 pkj + * Corrected spelling mistakes originally found in 2.5.x + * + * Revision 1.29 2003/06/17 13:57:49 starvik + * Merge of Linux 2.4.21 + * + * Revision 1.28 2003/01/22 12:41:12 starvik + * Added LBA48 support + * Fixed typo in e100_ideproc + * + * Revision 1.27 2003/01/09 18:03:47 starvik + * init_ioremap is now called by kernel before device drivers are initialized + * * Revision 1.26 2002/09/17 12:16:59 bjornw * Removed unnecessary cli/sti pair * @@ -151,6 +164,8 @@ static volatile unsigned long *reset_addr; #endif +static int e100_read_command = 0; + #define LOWDB(x) #define D(x) @@ -186,14 +201,9 @@ IO_MASK(R_ATA_STATUS_DATA, tr_rdy))) timeleft--; } -unsigned char -IN_BYTE(ide_ioreg_t reg) { - /* data was in the lower 16 bits in the status reg */ - return (unsigned char)(IN_WORD(reg)); -} unsigned short -IN_WORD(ide_ioreg_t reg) { +IN_BYTE(ide_ioreg_t reg) { int status; int timeleft; @@ -237,7 +247,7 @@ LOWDB(printk("inb: 0x%x from reg 0x%x\n", status & 0xff, reg)); - return (unsigned short)status; + return (unsigned char)status; } /* PIO timing (in R_ATA_CONFIG) @@ -292,9 +302,15 @@ #define ATA_PIO0_STROBE 19 #define ATA_PIO0_HOLD 4 -static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive); -static void e100_ideproc (ide_ide_action_t func, ide_drive_t *drive, - void *buffer, unsigned int length); +static int e100_dma_check (ide_drive_t *drive); +static int e100_dma_begin (ide_drive_t *drive); +static int e100_dma_end (ide_drive_t *drive); +static int e100_dma_read (ide_drive_t *drive); +static int e100_dma_write (ide_drive_t *drive); +static void e100_ide_input_data (ide_drive_t *drive, void *, unsigned int); +static void e100_ide_output_data (ide_drive_t *drive, void *, unsigned int); +static void e100_atapi_input_bytes(ide_drive_t *drive, void *, unsigned int); +static void e100_atapi_output_bytes(ide_drive_t *drive, void *, unsigned int); /* * good_dma_drives() lists the model names (from "hdparm -i") @@ -309,8 +325,6 @@ static void tune_e100_ide(ide_drive_t *drive, byte pio) { - unsigned long flags; - pio = 4; /* pio = ide_get_best_pio_mode(drive, pio, 4, NULL); */ @@ -374,8 +388,15 @@ ide_hwif_t *hwif = &ide_hwifs[h]; hwif->chipset = ide_etrax100; hwif->tuneproc = &tune_e100_ide; - hwif->dmaproc = &e100_dmaproc; - hwif->ideproc = &e100_ideproc; + hwif->ata_input_data = &e100_ide_input_data; + hwif->ata_output_data = &e100_ide_output_data; + hwif->atapi_input_bytes = &e100_atapi_input_bytes; + hwif->atapi_output_bytes = &e100_atapi_output_bytes; + hwif->ide_dma_check = &e100_dma_check; + hwif->ide_dma_end = &e100_dma_end; + hwif->ide_dma_write = &e100_dma_write; + hwif->ide_dma_read = &e100_dma_read; + hwif->ide_dma_begin = &e100_dma_begin; } /* actually reset and configure the etrax100 ide/ata interface */ @@ -400,11 +421,9 @@ REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, 27, 0); #endif #ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET - init_ioremap(); REG_SHADOW_SET(port_cse1_addr, port_cse1_shadow, 16, 0); #endif #ifdef CONFIG_ETRAX_IDE_CSP0_8_RESET - init_ioremap(); REG_SHADOW_SET(port_csp0_addr, port_csp0_shadow, 8, 0); #endif #ifdef CONFIG_ETRAX_IDE_PB7_RESET @@ -618,7 +637,7 @@ LED_DISK_WRITE(1); /* Etrax will set busy = 1 until the multi pio transfer has finished - * and tr_rdy = 1 after each succesful word transfer. + * and tr_rdy = 1 after each successful word transfer. * When the last byte has been transferred Etrax will first set tr_tdy = 1 * and then busy = 0 (not in the same cycle). If we read busy before it * has been set to 0 we will think that we should transfer more bytes @@ -655,32 +674,6 @@ e100_atapi_output_bytes(drive, buffer, wcount << 2); } -/* - * The multiplexor for ide_xxxput_data and atapi calls - */ -static void -e100_ideproc (ide_ide_action_t func, ide_drive_t *drive, - void *buffer, unsigned int length) -{ - switch (func) { - case ideproc_ide_input_data: - e100_ide_input_data(drive, buffer, length); - break; - case ideproc_ide_output_data: - e100_ide_input_data(drive, buffer, length); - break; - case ideproc_atapi_input_bytes: - e100_atapi_input_bytes(drive, buffer, length); - break; - case ideproc_atapi_output_bytes: - e100_atapi_output_bytes(drive, buffer, length); - break; - default: - printk("e100_ideproc: unsupported func %d!\n", func); - break; - } -} - /* we only have one DMA channel on the chip for ATA, so we can keep these statically */ static etrax_dma_descr ata_descrs[MAX_DMA_DESCRS]; static unsigned int ata_tot_size; @@ -815,8 +808,8 @@ LED_DISK_READ(0); LED_DISK_WRITE(0); - dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive); - stat = GET_STAT(); /* get drive status */ + dma_stat = HWIF(drive)->ide_dma_end(drive); + stat = HWIF(drive)->INB(IDE_STATUS_REG); /* get drive status */ if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { if (!dma_stat) { struct request *rq; @@ -833,7 +826,7 @@ } /* - * e100_dmaproc() initiates/aborts DMA read/write operations on a drive. + * Functions below initiates/aborts DMA read/write operations on a drive. * * The caller is assumed to have selected the drive and programmed the drive's * sector address using CHS or LBA. All that remains is to prepare for DMA @@ -848,59 +841,19 @@ * the caller should revert to PIO for the current request. */ -static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +static int e100_dma_check(ide_drive_t *drive) { - static unsigned int reading; /* static to support ide_dma_begin semantics */ - int atapi = 0; - - D(printk("e100_dmaproc func %d\n", func)); - - switch (func) { - case ide_dma_verbose: - return 0; - case ide_dma_check: - return config_drive_for_dma (drive); - case ide_dma_off: - case ide_dma_off_quietly: - /* ok.. we don't really need to do anything I think. */ - return 0; - case ide_dma_write: - reading = 0; - break; - case ide_dma_read: - reading = 1; - break; - case ide_dma_begin: - /* begin DMA, used by ATAPI devices which want to issue the - * appropriate IDE command themselves. - * - * they have already called ide_dma_read/write to set the - * static reading flag, now they call ide_dma_begin to do - * the real stuff. we tell our code below not to issue - * any IDE commands itself and jump into it. - */ - atapi++; - goto dma_begin; - case ide_dma_end: /* returns 1 on error, 0 otherwise */ - /* TODO: check if something went wrong with the DMA */ - return 0; - - default: - printk("e100_dmaproc: unsupported func %d\n", func); - return 1; - } - - /* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction - * then they call ide_dma_begin after they have issued the appropriate drive command - * themselves to actually start the chipset DMA. so we just return here if we're - * not a diskdrive. - */ - - if (drive->media != ide_disk) - return 0; + return config_drive_for_dma (drive); +} - dma_begin: +static int e100_dma_end(ide_drive_t *drive) +{ + /* TODO: check if something went wrong with the DMA */ + return 0; +} +static int e100_start_dma(ide_drive_t *drive, int atapi, int reading) +{ if(reading) { RESET_DMA(ATA_RX_DMA_NBR); /* sometimes the DMA channel get stuck so we need to do this */ @@ -917,8 +870,15 @@ ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL); /* issue cmd to drive */ - - OUT_BYTE(WIN_READDMA, IDE_COMMAND_REG); + if ((HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) && + (drive->addressing == 1)) { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } else if (drive->addressing) { + OUT_BYTE(WIN_READDMA_EXT, IDE_COMMAND_REG); + } else { + OUT_BYTE(WIN_READDMA, IDE_COMMAND_REG); + } } /* begin DMA */ @@ -968,8 +928,15 @@ ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL); /* issue cmd to drive */ - - OUT_BYTE(WIN_WRITEDMA, IDE_COMMAND_REG); + if ((HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) && + (drive->addressing == 1)) { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } else if (drive->addressing) { + OUT_BYTE(WIN_WRITEDMA_EXT, IDE_COMMAND_REG); + } else { + OUT_BYTE(WIN_WRITEDMA, IDE_COMMAND_REG); + } } /* begin DMA */ @@ -994,14 +961,44 @@ D(printk("dma write of %d bytes.\n", ata_tot_size)); } - - /* DMA started successfully */ return 0; } -/* ide.c calls this, but we don't need to do anything particular */ +static int e100_dma_write(ide_drive_t *drive) +{ + e100_read_command = 0; + /* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction + * then they call ide_dma_begin after they have issued the appropriate drive command + * themselves to actually start the chipset DMA. so we just return here if we're + * not a diskdrive. + */ + if (drive->media != ide_disk) + return 0; + return e100_start_dma(drive, 0, 0); +} + +static int e100_dma_read(ide_drive_t *drive) +{ + e100_read_command = 1; + /* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction + * then they call ide_dma_begin after they have issued the appropriate drive command + * themselves to actually start the chipset DMA. so we just return here if we're + * not a diskdrive. + */ + if (drive->media != ide_disk) + return 0; + return e100_start_dma(drive, 0, 1); +} -int ide_release_dma (ide_hwif_t *hwif) +static int e100_dma_begin(ide_drive_t *drive) { - return 1; + /* begin DMA, used by ATAPI devices which want to issue the + * appropriate IDE command themselves. + * + * they have already called ide_dma_read/write to set the + * static reading flag, now they call ide_dma_begin to do + * the real stuff. we tell our code below not to issue + * any IDE commands itself and jump into it. + */ + return e100_start_dma(drive, 1, e100_read_command); } diff -urN linux-2.4.21-bk5/arch/cris/drivers/lpslave/bintocarr.pl linux-2.4.21-bk6/arch/cris/drivers/lpslave/bintocarr.pl --- linux-2.4.21-bk5/arch/cris/drivers/lpslave/bintocarr.pl 2001-10-08 11:43:54.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/lpslave/bintocarr.pl 2003-07-10 02:54:58.000000000 -0700 @@ -1,5 +1,5 @@ #!/usr/bin/perl -w -# $Id: bintocarr.pl,v 1.4 2001/08/08 08:18:13 bjarne Exp $ + # Copy of mkjulbin.pl made by Olof # convert a binary stdin to a C-file containing a char array of the input # first argument is the symbol name @@ -27,4 +27,3 @@ $lensymb = ("_" . ($symbol . "_length")); print "__asm__(\"\\t.globl $lensymb\\n$lensymb = $bcount\\n\");\n"; - diff -urN linux-2.4.21-bk5/arch/cris/drivers/lpslave/e100lpslave.S linux-2.4.21-bk6/arch/cris/drivers/lpslave/e100lpslave.S --- linux-2.4.21-bk5/arch/cris/drivers/lpslave/e100lpslave.S 2001-07-26 15:10:06.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/lpslave/e100lpslave.S 2003-07-10 02:54:58.000000000 -0700 @@ -1,12 +1,11 @@ - ;; $Id: e100lpslave.S,v 1.3 2001/06/21 16:55:26 olof Exp $ - ;; - ;; Etrax100 slave network<->parport forwarder - ;; - ;; Copyright (c) 1999 Bjorn Wesen, Axis Communications AB - ;; - ;; We got 784 bytes (par loader size) to do DMA forwarding - ;; between DMA0/1 (ethernet) and DMA3/4 (par port 0 RX/1 TX) - ;; +;; +;; Etrax100 slave network<->parport forwarder +;; +;; Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB +;; +;; We got 784 bytes (par loader size) to do DMA forwarding +;; between DMA0/1 (ethernet) and DMA3/4 (par port 0 RX/1 TX) +;; #include #if 0 diff -urN linux-2.4.21-bk5/arch/cris/drivers/parport.c linux-2.4.21-bk6/arch/cris/drivers/parport.c --- linux-2.4.21-bk5/arch/cris/drivers/parport.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/parport.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,13 +1,13 @@ -/* $Id: parport.c,v 1.10 2002/10/03 05:54:24 starvik Exp $ - * - * Elinux parallel port driver +/* + * Parallel port driver for ETRAX. + * * NOTE! * Since par0 shares DMA with ser2 and par 1 shares DMA with ser3 * this should be handled if both are enabled at the same time. * THIS IS NOT HANDLED YET! * - * Copyright (c) 2001 Axis Communications AB - * + * Copyright (c) 2001, 2002, 2003 Axis Communications AB + * * Author: Fredrik Hugosson * */ @@ -406,7 +406,7 @@ static void __init parport_etrax_show_parallel_version(void) { - printk("ETRAX 100LX parallel port driver v1.0, (c) 2001 Axis Communications AB\n"); + printk("ETRAX 100LX parallel port driver v1.0, (c) 2001-2003 Axis Communications AB\n"); } #ifdef CONFIG_ETRAX_PAR0_DMA diff -urN linux-2.4.21-bk5/arch/cris/drivers/pcf8563.c linux-2.4.21-bk6/arch/cris/drivers/pcf8563.c --- linux-2.4.21-bk5/arch/cris/drivers/pcf8563.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/pcf8563.c 2003-07-10 02:54:58.000000000 -0700 @@ -8,14 +8,13 @@ * 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. + * automatically after each written or read byte. * - * Copyright (c) 2002, Axis Communications AB + * Copyright (c) 2002-2003, Axis Communications AB * All rights reserved. * * Author: Tobias Anderberg . * - * $Id: pcf8563.c,v 1.4 2002/10/15 09:22:42 tobiasa Exp $ */ #include @@ -36,19 +35,15 @@ #include #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.4 $" - -/* I2C bus slave registers. */ -#define RTC_I2C_READ 0xa3 -#define RTC_I2C_WRITE 0xa2 +#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.15 $" /* 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 }; @@ -64,53 +59,73 @@ }; unsigned char -pcf8563_readreg(int reg) +pcf8563_readreg(int reg) { - unsigned char res = i2c_readreg(RTC_I2C_READ, reg); + unsigned char res = rtc_read(reg); /* The PCF8563 does not return 0 for unimplemented bits */ - switch(reg) - { + switch (reg) { case RTC_SECONDS: case RTC_MINUTES: - res &= 0x7f; - break; + res &= 0x7F; + break; case RTC_HOURS: case RTC_DAY_OF_MONTH: - res &= 0x3f; - break; + res &= 0x3F; + break; + case RTC_WEEKDAY: + res &= 0x07; + break; case RTC_MONTH: - res = (res & 0x1f) - 1; /* PCF8563 returns month in range 1-12 */ - break; + res &= 0x1F; + break; + case RTC_CONTROL1: + res &= 0xA8; + break; + case RTC_CONTROL2: + res &= 0x1F; + break; + case RTC_CLOCKOUT_FREQ: + case RTC_TIMER_CONTROL: + res &= 0x83; + break; } return res; } void -pcf8563_writereg(int reg, unsigned char val) +pcf8563_writereg(int reg, unsigned char val) { - i2c_writereg(RTC_I2C_WRITE,reg,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_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_wday = rtc_read(RTC_WEEKDAY); + tm->tm_mon = rtc_read(RTC_MONTH); tm->tm_year = rtc_read(RTC_YEAR); if (tm->tm_sec & 0x80) - printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); - tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); - tm->tm_sec &= 0x7f; - tm->tm_min &= 0x7f; - tm->tm_hour &= 0x3f; - tm->tm_mday &= 0x3f; - tm->tm_mon &= 0x1f; + tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); + tm->tm_sec &= 0x7F; + tm->tm_min &= 0x7F; + tm->tm_hour &= 0x3F; + tm->tm_mday &= 0x3F; + tm->tm_wday &= 0x07; /* Not coded in BCD. */ + tm->tm_mon &= 0x1F; BCD_TO_BIN(tm->tm_sec); BCD_TO_BIN(tm->tm_min); @@ -124,10 +139,14 @@ pcf8563_init(void) { unsigned char ret; + + /* Initiate the i2c protocol. */ + i2c_init(); + /* * First of all we need to reset the chip. This is done by - * clearing control1, control2 and clk freq, clear the - * Voltage Low bit, and resetting all alarms. + * clearing control1, control2 and clk freq and resetting + * all alarms. */ if (rtc_write(RTC_CONTROL1, 0x00) < 0) goto err; @@ -138,37 +157,35 @@ 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) + if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0) goto err; - + /* Reset the alarms. */ - if (rtc_write(RTC_MINUTE_ALARM, 0x00) < 0) + if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_HOUR_ALARM, 0x00) < 0) + + if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_DAY_ALARM, 0x00) < 0) + + if (rtc_write(RTC_DAY_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_WEEKDAY_ALARM, 0x00) < 0) + + if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) goto err; if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { - printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n", + printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n", PCF8563_NAME, PCF8563_MAJOR); return -1; } printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); - + /* Check for low voltage, and warn about it.. */ if (rtc_read(RTC_SECONDS) & 0x80) - printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); - + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + return 0; err: @@ -200,75 +217,113 @@ switch (cmd) { case RTC_RD_TIME: - { - struct rtc_time tm; + { + struct rtc_time tm; - get_rtc_time(&tm); + get_rtc_time(&tm); - if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) { - return -EFAULT; - } - - return 0; + if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) { + return -EFAULT; } - break; - case RTC_SET_TIME: - { - int leap; - int century; - unsigned long flags; - struct rtc_time tm; - - 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; + return 0; + } + + case RTC_SET_TIME: + { +#ifdef CONFIG_ETRAX_RTC_READONLY + return -EPERM; +#else + int leap; + int year; + int century; + struct rtc_time tm; + + if (!capable(CAP_SYS_TIME)) + return -EPERM; + + if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm)) + return -EFAULT; + + /* Convert from struct tm to struct rtc_time. */ + tm.tm_year += 1900; + tm.tm_mon += 1; + + /* + * Check if tm.tm_year is a leap year. A year is a leap + * year if it is divisible by 4 but not 100, except + * that years divisible by 400 _are_ leap years. + */ + year = tm.tm_year; + leap = (tm.tm_mon == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); + + /* Perform some sanity checks. */ + if ((tm.tm_year < 1970) || + (tm.tm_mon > 12) || + (tm.tm_mday == 0) || + (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || + (tm.tm_wday >= 7) || + (tm.tm_hour >= 24) || + (tm.tm_min >= 60) || + (tm.tm_sec >= 60)) + return -EINVAL; + + century = (tm.tm_year >= 2000) ? 0x80 : 0; + tm.tm_year = tm.tm_year % 100; + + BIN_TO_BCD(tm.tm_year); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_sec); + tm.tm_mon |= century; + + rtc_write(RTC_YEAR, tm.tm_year); + rtc_write(RTC_MONTH, tm.tm_mon); + rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ + rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); + rtc_write(RTC_HOURS, tm.tm_hour); + rtc_write(RTC_MINUTES, tm.tm_min); + rtc_write(RTC_SECONDS, tm.tm_sec); + + return 0; +#endif /* !CONFIG_ETRAX_RTC_READONLY */ + } + + case RTC_VLOW_RD: + { + int vl_bit = 0; + + if (rtc_read(RTC_SECONDS) & 0x80) { + vl_bit = 1; + printk(KERN_WARNING "%s: RTC Voltage Low - reliable " + "date/time information is no longer guaranteed!\n", + PCF8563_NAME); } - break; + 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 -ENOTTY; } return 0; } -int +int pcf8563_open(struct inode *inode, struct file *filp) { MOD_INC_USE_COUNT; diff -urN linux-2.4.21-bk5/arch/cris/drivers/serial.c linux-2.4.21-bk6/arch/cris/drivers/serial.c --- linux-2.4.21-bk5/arch/cris/drivers/serial.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/serial.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,12 +1,92 @@ -/* $Id: serial.c,v 1.44 2002/11/21 04:28:06 hp Exp $ +/* $Id: serial.c,v 1.54 2003/07/08 12:42:19 johana Exp $ * * Serial port driver for the ETRAX 100LX chip * - * Copyright (C) 1998, 1999, 2000, 2001, 2002 Axis Communications AB + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Axis Communications AB * - * Many, many authors. Based once upon a time on serial.c for 16x50. + * Many, many authors. Based once upon a time on serial.c for 16x50. * * $Log: serial.c,v $ + * Revision 1.54 2003/07/08 12:42:19 johana + * Removed some test defines within #if 0. + * Moved a comment to correct place. + * + * Revision 1.53 2003/07/08 09:48:28 starvik + * Removed test code + * + * Revision 1.52 2003/07/08 07:24:47 pkj + * Corrected spelling mistakes originally found in 2.5.x + * + * Revision 1.51 2003/07/07 09:17:12 johana + * Config.in: + * Don't consider ETRAX_FAST_TIMER experimental. + * Add CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED + * Add CONFIG_ETRAX_EXTERN_PB6CLK_FREQ + * Add CONFIG_ETRAX_SERIAL_PORTx_DMAy_OUT/IN + * + * serial.h: + * Support for with and without DMA. + * Added baud_base and custom_divisor for flexible baudrate. + * Added flush_time_usec. + * Removed unused DMA register pointers. + * + * serial.c: + * Use C99 style initialisation of rs_table, some fields added, some removed. + * Always disable interrupts when fiddling with shadow variables. + * Support for flexible baudrate using prescaler or external clock. + * Only disable the DMA channel if we own it. + * Support for either using DMA or char-by-char interrupt. + * + * DMA changes: + * o When transmitting with DMA, don't put all in one descriptor, split + * it so that we can wake up the application before everything is sent. + * o When starting a fast timer to flip later, use a longer, fixed time. + * o Use flush_time_usec and have a minimum value to prevent excessive load + * on high baudrates. + * o Do force_eop_if_needed() after processing to reduce load. + * o All DMA interrupts uses SA_SHIRQ | SA_INTERRUPT + * + * Only have interrupts disabled while processing rx interrupts, + * unmask just tx interrupts and unblock serial interrupt and keep + * global interrupts enabled while processing tx interrupts to make + * char-by-char interrupts usable. + * + * Flow control: + * o Use normal tx interrupt when sending x_char. + * o Use throttling within the driver when using DMA. + * + * Improved debugging with DEBUG_LOG macro. + * + * Revision 1.50 2003/06/13 10:04:12 johana + * Help the user to avoid trouble by: + * Preventing FAST_TIMER and FAST_DMA_FLUSH at the same time. + * Forcing mixed mode for status/control lines if not all pins are used. + * + * Som minor changes to reduce diff to os/lx25 version. + * + * Revision 1.49 2003/05/30 11:31:54 johana + * Merged in change-branch--serial9bit that adds CMSPAR support for sticky + * parity (mark/space) + * + * Revision 1.48 2003/05/30 11:03:57 johana + * Implemented rs_send_xchar() by disabling the DMA and writing manually. + * Added e100_disable_txdma_channel() and e100_enable_txdma_channel(). + * Fixed rs_throttle() and rs_unthrottle() to properly call rs_send_xchar + * instead of setting info->x_char and check the CRTSCTS flag before + * controlling the rts pin. + * + * Revision 1.47 2003/04/09 08:31:14 pkj + * Typo correction (taken from Linux 2.5). + * + * Revision 1.46 2003/04/01 14:12:06 starvik + * Added loglevel for lots of printks + * + * Revision 1.45 2003/01/22 06:54:46 starvik + * Fixed warnings issued by GCC 3.2.1 + * + * Revision 1.44.4.1 2003/01/08 06:47:12 starvik + * Added mark/space parity (for 9-bit serial) + * * Revision 1.44 2002/11/21 04:28:06 hp * Change static inline to extern inline where otherwise outlined with gcc-3.2 * @@ -206,7 +286,7 @@ * Items worth noticing: * * No Etrax100 port 1 workarounds (does only compile on 2.4 anyway now) - * RS485 is not ported (why cant it be done in userspace as on x86 ?) + * RS485 is not ported (why can't it be done in userspace as on x86 ?) * Statistics done through async_icount - if any more stats are needed, * that's the place to put them or in an arch-dep version of it. * timeout_interrupt and the other fast timeout stuff not ported yet @@ -357,7 +437,7 @@ * */ -static char *serial_version = "$Revision: 1.44 $"; +static char *serial_version = "$Revision: 1.54 $"; #include #include @@ -392,7 +472,7 @@ #include -/* non-arch dependant serial structures are in linux/serial.h */ +/* non-arch dependent serial structures are in linux/serial.h */ #include /* while we keep our own stuff (struct e100_serial) in a local .h file */ #include "serial.h" @@ -406,6 +486,11 @@ #error "Enable FAST_TIMER to use SERIAL_FAST_TIMER" #endif #endif + +#if defined(CONFIG_ETRAX_FAST_TIMER) && defined(CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST) +#error "Disable either ETRAX_SERIAL_FLUSH_DMA_FAST or ETRAX_FAST_TIMER" +#endif + /* * All of the compatibilty code so we can compile serial.c against * older kernels is hidden in serial_compat.h @@ -414,8 +499,11 @@ #include "serial_compat.h" #endif +#if 1 /* Set to 0 to avoid inlining - easier to see how size of parts */ #define _INLINE_ inline - +#else +#define _INLINE_ +#endif static DECLARE_TASK_QUEUE(tq_serial); struct tty_driver serial_driver, callout_driver; @@ -436,7 +524,7 @@ //#define SERIAL_DEBUG_DATA //#define SERIAL_DEBUG_THROTTLE //#define SERIAL_DEBUG_IO /* Debug for Extra control and status pins */ -#define SERIAL_DEBUG_LINE 0 /* What serport we want to debug */ +#define SERIAL_DEBUG_LINE 3 /* What serport we want to debug */ /* Enable this to use serial interrupts to handle when you expect the first received event on the serial port to @@ -444,14 +532,78 @@ from eLinux */ #define SERIAL_HANDLE_EARLY_ERRORS -#define TTY_THROTTLE_LIMIT (TTY_FLIPBUF_SIZE/10) +/* Defined and used in n_tty.c, but we need it here as well */ +#define TTY_THRESHOLD_THROTTLE 128 +/* Due to buffersizes and threshold values, our SERIAL_DESCR_BUF_SIZE + * must not be to high or flow control won't work if we leave it to the tty + * layer so we have our own throttling in flush_to_flip + * TTY_FLIPBUF_SIZE=512, + * TTY_THRESHOLD_THROTTLE/UNTHROTTLE=128 + * BUF_SIZE can't be > 128 + */ +/* Currently 16 descriptors x 128 bytes = 2048 bytes */ #define SERIAL_DESCR_BUF_SIZE 256 +#define SERIAL_PRESCALE_BASE 3125000 /* 3.125MHz */ +#define DEF_BAUD_BASE SERIAL_PRESCALE_BASE + +/* We don't want to load the system with massive fast timer interrupt + * on high baudrates so limit it to 250 us (4kHz) */ +#define MIN_FLUSH_TIME_USEC 250 + /* Add an x here to log a lot of timer stuff */ #define TIMERD(x) +/* Debug details of interrupt handling */ +#define DINTR1(x) /* irq on/off, errors */ +#define DINTR2(x) /* tx and rx */ +/* Debug flip buffer stuff */ +#define DFLIP(x) +/* Debug flow control and overview of data flow */ +#define DFLOW(x) +#define DBAUD(x) +#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT +#define DLOG_INT_TRIG(x) +#else +#define DLOG_INT_TRIG(x) +#endif +//#define DEBUG_LOG_INCLUDED +#ifndef DEBUG_LOG_INCLUDED #define DEBUG_LOG(line, string, value) +#else +struct debug_log_info +{ + unsigned long time; + unsigned long timer_data; +// int line; + const char *string; + int value; +}; +#define DEBUG_LOG_SIZE 4096 + +struct debug_log_info debug_log[DEBUG_LOG_SIZE]; +int debug_log_pos = 0; + +#define DEBUG_LOG(_line, _string, _value) do { \ + if ((_line) == SERIAL_DEBUG_LINE) {\ + debug_log_func(_line, _string, _value); \ + }\ +}while(0) + +void debug_log_func(int line, const char *string, int value) +{ + if (debug_log_pos < DEBUG_LOG_SIZE) { + debug_log[debug_log_pos].time = jiffies; + debug_log[debug_log_pos].timer_data = *R_TIMER_DATA; +// debug_log[debug_log_pos].line = line; + debug_log[debug_log_pos].string = string; + debug_log[debug_log_pos].value = value; + debug_log_pos++; + } + /*printk(string, value);*/ +} +#endif #ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS /* Default number of timer ticks before flushing rx fifo @@ -461,21 +613,23 @@ #define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5 #endif +unsigned long timer_data_to_ns(unsigned long timer_data); + static void change_speed(struct e100_serial *info); +static void rs_throttle(struct tty_struct * tty); static void rs_wait_until_sent(struct tty_struct *tty, int timeout); static int rs_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count); -extern inline int raw_write(struct tty_struct * tty, int from_user, +extern _INLINE_ int rs_raw_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count); #ifdef CONFIG_ETRAX_RS485 static int e100_write_rs485(struct tty_struct * tty, int from_user, const unsigned char *buf, int count); #endif -static int -get_lsr_info(struct e100_serial * info, unsigned int *value); +static int get_lsr_info(struct e100_serial * info, unsigned int *value); -#define DEF_BAUD 0x99 /* 115.2 kbit/s */ +#define DEF_BAUD 115200 /* 115.2 kbit/s */ #define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) #define DEF_RX 0x20 /* or SERIAL_CTRL_W >> 8 */ /* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */ @@ -484,6 +638,7 @@ /* offsets from R_SERIALx_CTRL */ #define REG_DATA 0 +#define REG_DATA_STATUS32 0 /* this is the 32 bit register R_SERIALx_READ */ #define REG_TR_DATA 0 #define REG_STATUS 1 #define REG_TR_CTRL 1 @@ -519,60 +674,162 @@ */ +/* Mask for the irqs possibly enabled in R_IRQ_MASK1_RD etc. */ +static const unsigned long e100_ser_int_mask = 0 +#ifdef CONFIG_ETRAX_SERIAL_PORT0 +| IO_MASK(R_IRQ_MASK1_RD, ser0_data) | IO_MASK(R_IRQ_MASK1_RD, ser0_ready) +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT1 +| IO_MASK(R_IRQ_MASK1_RD, ser1_data) | IO_MASK(R_IRQ_MASK1_RD, ser1_ready) +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT2 +| IO_MASK(R_IRQ_MASK1_RD, ser2_data) | IO_MASK(R_IRQ_MASK1_RD, ser2_ready) +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT3 +| IO_MASK(R_IRQ_MASK1_RD, ser3_data) | IO_MASK(R_IRQ_MASK1_RD, ser3_ready) +#endif +; +unsigned long r_alt_ser_baudrate_shadow = 0; + /* this is the data for the four serial ports in the etrax100 */ /* DMA2(ser2), DMA4(ser3), DMA6(ser0) or DMA8(ser1) */ /* R_DMA_CHx_CLR_INTR, R_DMA_CHx_FIRST, R_DMA_CHx_CMD */ static struct e100_serial rs_table[] = { - { DEF_BAUD, (unsigned char *)R_SERIAL0_CTRL, 1U << 12, /* uses DMA 6 and 7 */ - R_DMA_CH6_CLR_INTR, R_DMA_CH6_FIRST, R_DMA_CH6_CMD, - R_DMA_CH6_STATUS, R_DMA_CH6_HWSW, R_DMA_CH6_DESCR, - R_DMA_CH7_CLR_INTR, R_DMA_CH7_FIRST, R_DMA_CH7_CMD, - R_DMA_CH7_STATUS, R_DMA_CH7_HWSW, R_DMA_CH7_DESCR, - STD_FLAGS, DEF_RX, DEF_TX, 2, + { .baud = DEF_BAUD, + .port = (unsigned char *)R_SERIAL0_CTRL, + .irq = 1U << 12, /* uses DMA 6 and 7 */ + .oclrintradr = R_DMA_CH6_CLR_INTR, + .ofirstadr = R_DMA_CH6_FIRST, + .ocmdadr = R_DMA_CH6_CMD, + .ostatusadr = R_DMA_CH6_STATUS, + .iclrintradr = R_DMA_CH7_CLR_INTR, + .ifirstadr = R_DMA_CH7_FIRST, + .icmdadr = R_DMA_CH7_CMD, + .idescradr = R_DMA_CH7_DESCR, + .flags = STD_FLAGS, + .rx_ctrl = DEF_RX, + .tx_ctrl = DEF_TX, + .iseteop = 2, #ifdef CONFIG_ETRAX_SERIAL_PORT0 - 1 + .enabled = 1, +#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT + .dma_out_enabled = 1, +#else + .dma_out_enabled = 0, +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN + .dma_in_enabled = 1, +#else + .dma_in_enabled = 0 +#endif #else - 0 + .enabled = 0, + .dma_out_enabled = 0, + .dma_in_enabled = 0 #endif + }, /* ttyS0 */ #ifndef CONFIG_SVINTO_SIM - { DEF_BAUD, (unsigned char *)R_SERIAL1_CTRL, 1U << 16, /* uses DMA 8 and 9 */ - R_DMA_CH8_CLR_INTR, R_DMA_CH8_FIRST, R_DMA_CH8_CMD, - R_DMA_CH8_STATUS, R_DMA_CH8_HWSW, R_DMA_CH8_DESCR, - R_DMA_CH9_CLR_INTR, R_DMA_CH9_FIRST, R_DMA_CH9_CMD, - R_DMA_CH9_STATUS, R_DMA_CH9_HWSW, R_DMA_CH9_DESCR, - STD_FLAGS, DEF_RX, DEF_TX, 3 , + { .baud = DEF_BAUD, + .port = (unsigned char *)R_SERIAL1_CTRL, + .irq = 1U << 16, /* uses DMA 8 and 9 */ + .oclrintradr = R_DMA_CH8_CLR_INTR, + .ofirstadr = R_DMA_CH8_FIRST, + .ocmdadr = R_DMA_CH8_CMD, + .ostatusadr = R_DMA_CH8_STATUS, + .iclrintradr = R_DMA_CH9_CLR_INTR, + .ifirstadr = R_DMA_CH9_FIRST, + .icmdadr = R_DMA_CH9_CMD, + .idescradr = R_DMA_CH9_DESCR, + .flags = STD_FLAGS, + .rx_ctrl = DEF_RX, + .tx_ctrl = DEF_TX, + .iseteop = 3, #ifdef CONFIG_ETRAX_SERIAL_PORT1 - 1 + .enabled = 1, +#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT + .dma_out_enabled = 1, +#else + .dma_out_enabled = 0, +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN + .dma_in_enabled = 1, #else - 0 + .dma_in_enabled = 0 +#endif +#else + .enabled = 0, + .dma_out_enabled = 0, + .dma_in_enabled = 0 #endif }, /* ttyS1 */ - { DEF_BAUD, (unsigned char *)R_SERIAL2_CTRL, 1U << 4, /* uses DMA 2 and 3 */ - R_DMA_CH2_CLR_INTR, R_DMA_CH2_FIRST, R_DMA_CH2_CMD, - R_DMA_CH2_STATUS, R_DMA_CH2_HWSW, R_DMA_CH2_DESCR, - R_DMA_CH3_CLR_INTR, R_DMA_CH3_FIRST, R_DMA_CH3_CMD, - R_DMA_CH3_STATUS, R_DMA_CH3_HWSW, R_DMA_CH3_DESCR, - STD_FLAGS, DEF_RX, DEF_TX, 0, + { .baud = DEF_BAUD, + .port = (unsigned char *)R_SERIAL2_CTRL, + .irq = 1U << 4, /* uses DMA 2 and 3 */ + .oclrintradr = R_DMA_CH2_CLR_INTR, + .ofirstadr = R_DMA_CH2_FIRST, + .ocmdadr = R_DMA_CH2_CMD, + .ostatusadr = R_DMA_CH2_STATUS, + .iclrintradr = R_DMA_CH3_CLR_INTR, + .ifirstadr = R_DMA_CH3_FIRST, + .icmdadr = R_DMA_CH3_CMD, + .idescradr = R_DMA_CH3_DESCR, + .flags = STD_FLAGS, + .rx_ctrl = DEF_RX, + .tx_ctrl = DEF_TX, + .iseteop = 0, #ifdef CONFIG_ETRAX_SERIAL_PORT2 - 1 + .enabled = 1, +#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT + .dma_out_enabled = 1, +#else + .dma_out_enabled = 0, +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN + .dma_in_enabled = 1, #else - 0 + .dma_in_enabled = 0 +#endif +#else + .enabled = 0, + .dma_out_enabled = 0, + .dma_in_enabled = 0 #endif }, /* ttyS2 */ - { DEF_BAUD, (unsigned char *)R_SERIAL3_CTRL, 1U << 8, /* uses DMA 4 and 5 */ - R_DMA_CH4_CLR_INTR, R_DMA_CH4_FIRST, R_DMA_CH4_CMD, - R_DMA_CH4_STATUS, R_DMA_CH4_HWSW, R_DMA_CH4_DESCR, - R_DMA_CH5_CLR_INTR, R_DMA_CH5_FIRST, R_DMA_CH5_CMD, - R_DMA_CH5_STATUS, R_DMA_CH5_HWSW, R_DMA_CH5_DESCR, - STD_FLAGS, DEF_RX, DEF_TX, 1, + { .baud = DEF_BAUD, + .port = (unsigned char *)R_SERIAL3_CTRL, + .irq = 1U << 8, /* uses DMA 4 and 5 */ + .oclrintradr = R_DMA_CH4_CLR_INTR, + .ofirstadr = R_DMA_CH4_FIRST, + .ocmdadr = R_DMA_CH4_CMD, + .ostatusadr = R_DMA_CH4_STATUS, + .iclrintradr = R_DMA_CH5_CLR_INTR, + .ifirstadr = R_DMA_CH5_FIRST, + .icmdadr = R_DMA_CH5_CMD, + .idescradr = R_DMA_CH5_DESCR, + .flags = STD_FLAGS, + .rx_ctrl = DEF_RX, + .tx_ctrl = DEF_TX, + .iseteop = 1, #ifdef CONFIG_ETRAX_SERIAL_PORT3 - 1 + .enabled = 1, +#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT + .dma_out_enabled = 1, +#else + .dma_out_enabled = 0, +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN + .dma_in_enabled = 1, #else - 0 + .dma_in_enabled = 0 +#endif +#else + .enabled = 0, + .dma_out_enabled = 0, + .dma_in_enabled = 0 #endif } /* ttyS3 */ #endif @@ -627,7 +884,7 @@ ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \ (R_PORT_PA_DATA): ( \ (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \ - (R_PORT_PB_DATA):&dummy_ser[##line##])) + (R_PORT_PB_DATA):&dummy_ser[line])) #define E100_STRUCT_SHADOW(line, pinname) \ ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \ @@ -646,6 +903,225 @@ #define DUMMY_CD_MASK 8 static unsigned char dummy_ser[NR_PORTS] = {0xFF, 0xFF, 0xFF,0xFF}; +/* If not all status pins are used or disabled, use mixed mode */ +#ifdef CONFIG_ETRAX_SERIAL_PORT0 + +#define SER0_PA_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PA_BIT+CONFIG_ETRAX_SER0_RI_ON_PA_BIT+CONFIG_ETRAX_SER0_DSR_ON_PA_BIT+CONFIG_ETRAX_SER0_CD_ON_PA_BIT) + +#if SER0_PA_BITSUM != -4 +# if CONFIG_ETRAX_SER0_DTR_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER0_RI_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER0_DSR_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER0_CD_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +#endif + +#define SER0_PB_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PB_BIT+CONFIG_ETRAX_SER0_RI_ON_PB_BIT+CONFIG_ETRAX_SER0_DSR_ON_PB_BIT+CONFIG_ETRAX_SER0_CD_ON_PB_BIT) + +#if SER0_PB_BITSUM != -4 +# if CONFIG_ETRAX_SER0_DTR_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER0_RI_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER0_DSR_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER0_CD_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +#endif + +#endif /* PORT0 */ + + +#ifdef CONFIG_ETRAX_SERIAL_PORT1 + +#define SER1_PA_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PA_BIT+CONFIG_ETRAX_SER1_RI_ON_PA_BIT+CONFIG_ETRAX_SER1_DSR_ON_PA_BIT+CONFIG_ETRAX_SER1_CD_ON_PA_BIT) + +#if SER1_PA_BITSUM != -4 +# if CONFIG_ETRAX_SER1_DTR_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER1_RI_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER1_DSR_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER1_CD_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +#endif + +#define SER1_PB_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PB_BIT+CONFIG_ETRAX_SER1_RI_ON_PB_BIT+CONFIG_ETRAX_SER1_DSR_ON_PB_BIT+CONFIG_ETRAX_SER1_CD_ON_PB_BIT) + +#if SER1_PB_BITSUM != -4 +# if CONFIG_ETRAX_SER1_DTR_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER1_RI_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER1_DSR_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER1_CD_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +#endif + +#endif /* PORT1 */ + +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + +#define SER2_PA_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PA_BIT+CONFIG_ETRAX_SER2_RI_ON_PA_BIT+CONFIG_ETRAX_SER2_DSR_ON_PA_BIT+CONFIG_ETRAX_SER2_CD_ON_PA_BIT) + +#if SER2_PA_BITSUM != -4 +# if CONFIG_ETRAX_SER2_DTR_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER2_RI_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER2_DSR_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER2_CD_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +#endif + +#define SER2_PB_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PB_BIT+CONFIG_ETRAX_SER2_RI_ON_PB_BIT+CONFIG_ETRAX_SER2_DSR_ON_PB_BIT+CONFIG_ETRAX_SER2_CD_ON_PB_BIT) + +#if SER2_PB_BITSUM != -4 +# if CONFIG_ETRAX_SER2_DTR_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER2_RI_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER2_DSR_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER2_CD_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +#endif + +#endif /* PORT2 */ + +#ifdef CONFIG_ETRAX_SERIAL_PORT3 + +#define SER3_PA_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PA_BIT+CONFIG_ETRAX_SER3_RI_ON_PA_BIT+CONFIG_ETRAX_SER3_DSR_ON_PA_BIT+CONFIG_ETRAX_SER3_CD_ON_PA_BIT) + +#if SER3_PA_BITSUM != -4 +# if CONFIG_ETRAX_SER3_DTR_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER3_RI_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER3_DSR_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER3_CD_ON_PA_BIT == -1 +# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +#endif + +#define SER3_PB_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PB_BIT+CONFIG_ETRAX_SER3_RI_ON_PB_BIT+CONFIG_ETRAX_SER3_DSR_ON_PB_BIT+CONFIG_ETRAX_SER3_CD_ON_PB_BIT) + +#if SER3_PB_BITSUM != -4 +# if CONFIG_ETRAX_SER3_DTR_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER3_RI_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER3_DSR_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +# if CONFIG_ETRAX_SER3_CD_ON_PB_BIT == -1 +# ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED +# define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1 +# endif +# endif +#endif + +#endif /* PORT3 */ + + #if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED) || \ defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED) || \ defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED) || \ @@ -653,6 +1129,7 @@ #define CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED #endif + #ifdef CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED /* The pins can be mixed on PA and PB */ #define CONTROL_PINS_PORT_NOT_USED(line) \ @@ -838,7 +1315,6 @@ #define E100_RTS_MASK 0x20 #define E100_CTS_MASK 0x40 - /* All serial port signals are active low: * active = 0 -> 3.3V to RS-232 driver -> -12V on RS-232 level * inactive = 1 -> 0V to RS-232 driver -> +12V on RS-232 level @@ -937,6 +1413,9 @@ /* calc timeout */ info->char_time_usec = ((bits * 1000000) / info->baud) + 1; + info->flush_time_usec = 4*info->char_time_usec; + if (info->flush_time_usec < MIN_FLUSH_TIME_USEC) + info->flush_time_usec = MIN_FLUSH_TIME_USEC; } /* @@ -980,7 +1459,7 @@ retval = baud_table[cflag & CBAUD]; if (retval < 0) { - printk("serdriver tried setting invalid baud rate, flags %x.\n", cflag); + printk(KERN_WARNING "serdriver tried setting invalid baud rate, flags %x.\n", cflag); retval = 5; /* choose default 9600 instead */ } @@ -1036,9 +1515,13 @@ e100_rts(struct e100_serial *info, int set) { #ifndef CONFIG_SVINTO_SIM + unsigned long flags; + save_flags(flags); + cli(); info->rx_ctrl &= ~E100_RTS_MASK; info->rx_ctrl |= (set ? 0 : E100_RTS_MASK); /* RTS is active low */ info->port[REG_REC_CTRL] = info->rx_ctrl; + restore_flags(flags); #ifdef SERIAL_DEBUG_IO printk("ser%i rts %i\n", info->line, set); #endif @@ -1112,6 +1595,7 @@ #ifdef SERIAL_DEBUG_INTR printk("rxdma_irq(%d): 0\n",info->line); #endif + DINTR1(DEBUG_LOG(info->line,"IRQ disable_rxdma_irq %i\n", info->line)); *R_IRQ_MASK2_CLR = (info->irq << 2) | (info->irq << 3); } @@ -1121,6 +1605,7 @@ #ifdef SERIAL_DEBUG_INTR printk("rxdma_irq(%d): 1\n",info->line); #endif + DINTR1(DEBUG_LOG(info->line,"IRQ enable_rxdma_irq %i\n", info->line)); *R_IRQ_MASK2_SET = (info->irq << 2) | (info->irq << 3); } @@ -1132,6 +1617,7 @@ #ifdef SERIAL_DEBUG_INTR printk("txdma_irq(%d): 0\n",info->line); #endif + DINTR1(DEBUG_LOG(info->line,"IRQ disable_txdma_irq %i\n", info->line)); *R_IRQ_MASK2_CLR = info->irq; } @@ -1141,9 +1627,143 @@ #ifdef SERIAL_DEBUG_INTR printk("txdma_irq(%d): 1\n",info->line); #endif + DINTR1(DEBUG_LOG(info->line,"IRQ enable_txdma_irq %i\n", info->line)); *R_IRQ_MASK2_SET = info->irq; } +static _INLINE_ void +e100_disable_txdma_channel(struct e100_serial *info) +{ + unsigned long flags; + + /* Disable output DMA channel for the serial port in question + * ( set to something other then serialX) + */ + save_flags(flags); + cli(); + DFLOW(DEBUG_LOG(info->line, "disable_txdma_channel %i\n", info->line)); + if (info->line == 0) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma6)) == + IO_STATE(R_GEN_CONFIG, dma6, serial0)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused); + } + } else if (info->line == 1) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma8)) == + IO_STATE(R_GEN_CONFIG, dma8, serial1)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb); + } + } else if (info->line == 2) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma2)) == + IO_STATE(R_GEN_CONFIG, dma2, serial2)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0); + } + } else if (info->line == 3) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma4)) == + IO_STATE(R_GEN_CONFIG, dma4, serial3)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma4); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1); + } + } + *R_GEN_CONFIG = genconfig_shadow; + restore_flags(flags); +} + + +static _INLINE_ void +e100_enable_txdma_channel(struct e100_serial *info) +{ + unsigned long flags; + + save_flags(flags); + cli(); + DFLOW(DEBUG_LOG(info->line, "enable_txdma_channel %i\n", info->line)); + /* Enable output DMA channel for the serial port in question */ + if (info->line == 0) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, serial0); + } else if (info->line == 1) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, serial1); + } else if (info->line == 2) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, serial2); + } else if (info->line == 3) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma4); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, serial3); + } + *R_GEN_CONFIG = genconfig_shadow; + restore_flags(flags); +} + +static _INLINE_ void +e100_disable_rxdma_channel(struct e100_serial *info) +{ + unsigned long flags; + + /* Disable input DMA channel for the serial port in question + * ( set to something other then serialX) + */ + save_flags(flags); + cli(); + if (info->line == 0) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma7)) == + IO_STATE(R_GEN_CONFIG, dma7, serial0)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma7); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, unused); + } + } else if (info->line == 1) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma9)) == + IO_STATE(R_GEN_CONFIG, dma9, serial1)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma9); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, usb); + } + } else if (info->line == 2) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma3)) == + IO_STATE(R_GEN_CONFIG, dma3, serial2)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma3); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, par0); + } + } else if (info->line == 3) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma5)) == + IO_STATE(R_GEN_CONFIG, dma5, serial3)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma5); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, par1); + } + } + *R_GEN_CONFIG = genconfig_shadow; + restore_flags(flags); +} + + +static _INLINE_ void +e100_enable_rxdma_channel(struct e100_serial *info) +{ + unsigned long flags; + + save_flags(flags); + cli(); + /* Enable input DMA channel for the serial port in question */ + if (info->line == 0) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma7); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, serial0); + } else if (info->line == 1) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma9); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, serial1); + } else if (info->line == 2) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma3); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, serial2); + } else if (info->line == 3) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma5); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, serial3); + } + *R_GEN_CONFIG = genconfig_shadow; + restore_flags(flags); +} + + #ifdef SERIAL_HANDLE_EARLY_ERRORS /* in order to detect and fix errors on the first byte we have to use the serial interrupts as well. */ @@ -1154,6 +1774,7 @@ #ifdef SERIAL_DEBUG_INTR printk("ser_irq(%d): 0\n",info->line); #endif + DINTR1(DEBUG_LOG(info->line,"IRQ disable data_irq %i\n", info->line)); *R_IRQ_MASK1_CLR = (1U << (8+2*info->line)); } @@ -1166,10 +1787,50 @@ (8+2*info->line), (1U << (8+2*info->line))); #endif + DINTR1(DEBUG_LOG(info->line,"IRQ enable data_irq %i\n", info->line)); *R_IRQ_MASK1_SET = (1U << (8+2*info->line)); } #endif +static inline void +e100_disable_serial_tx_ready_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("ser_tx_irq(%d): 0\n",info->line); +#endif + DINTR1(DEBUG_LOG(info->line,"IRQ disable ready_irq %i\n", info->line)); + *R_IRQ_MASK1_CLR = (1U << (8+1+2*info->line)); +} + +static inline void +e100_enable_serial_tx_ready_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("ser_tx_irq(%d): 1\n",info->line); + printk("**** %d = %d\n", + (8+1+2*info->line), + (1U << (8+1+2*info->line))); +#endif + DINTR2(DEBUG_LOG(info->line,"IRQ enable ready_irq %i\n", info->line)); + *R_IRQ_MASK1_SET = (1U << (8+1+2*info->line)); +} + +static inline void e100_enable_rx_irq(struct e100_serial *info) +{ + if (info->uses_dma_in) + e100_enable_rxdma_irq(info); + else + e100_enable_serial_data_irq(info); +} +static inline void e100_disable_rx_irq(struct e100_serial *info) +{ + if (info->uses_dma_in) + e100_disable_rxdma_irq(info); + else + e100_disable_serial_data_irq(info); +} + + #if defined(CONFIG_ETRAX_RS485) /* Enable RS-485 mode on selected port. This is UGLY. */ static int @@ -1183,7 +1844,10 @@ info->rs485.rts_on_send = 0x01 & r->rts_on_send; info->rs485.rts_after_sent = 0x01 & r->rts_after_sent; - info->rs485.delay_rts_before_send = r->delay_rts_before_send; + if (r->delay_rts_before_send >= 1000) + info->rs485.delay_rts_before_send = 1000; + else + info->rs485.delay_rts_before_send = r->delay_rts_before_send; info->rs485.enabled = r->enabled; /* printk("rts: on send = %i, after = %i, enabled = %i", info->rs485.rts_on_send, @@ -1223,7 +1887,7 @@ e100_rts(info, info->rs485.rts_after_sent); #if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) e100_enable_rx(info); - e100_enable_rxdma_irq(info); + e100_enable_rx_irq(info); #endif } #endif @@ -1245,8 +1909,12 @@ if (info) { unsigned long flags; unsigned long xoff; - + save_flags(flags); cli(); + DFLOW(DEBUG_LOG(info->line, "XOFF rs_stop xmit %i\n", + CIRC_CNT(info->xmit.head, + info->xmit.tail,SERIAL_XMIT_SIZE))); + xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->tty)); xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop); if (tty->termios->c_iflag & IXON ) { @@ -1267,6 +1935,9 @@ unsigned long xoff; save_flags(flags); cli(); + DFLOW(DEBUG_LOG(info->line, "XOFF rs_start xmit %i\n", + CIRC_CNT(info->xmit.head, + info->xmit.tail,SERIAL_XMIT_SIZE))); xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty)); xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable); if (tty->termios->c_iflag & IXON ) { @@ -1274,6 +1945,9 @@ } *((unsigned long *)&info->port[REG_XOFF]) = xoff; + if (!info->uses_dma_out && + info->xmit.head != info->xmit.tail && info->xmit.buf) + e100_enable_serial_tx_ready_irq(info); restore_flags(flags); } @@ -1308,6 +1982,8 @@ rs_sched_event(struct e100_serial *info, int event) { + if (info->event & (1 << event)) + return; info->event |= 1 << event; queue_task(&info->tqueue, &tq_serial); mark_bh(SERIAL_BH); @@ -1325,7 +2001,7 @@ */ static void -transmit_chars(struct e100_serial *info) +transmit_chars_dma(struct e100_serial *info) { unsigned int c, sentl; struct etrax_dma_descr *descr; @@ -1333,11 +2009,11 @@ #ifdef CONFIG_SVINTO_SIM /* This will output too little if tail is not 0 always since * we don't reloop to send the other part. Anyway this SHOULD be a - * no-op - transmit_chars would never really be called during sim + * no-op - transmit_chars_dma would never really be called during sim * since rs_write does not write into the xmit buffer then. */ if (info->xmit.tail) - printk("Error in serial.c:transmit_chars(), tail!=0\n"); + printk("Error in serial.c:transmit_chars_dma(), tail!=0\n"); if (info->xmit.head != info->xmit.tail) { SIMCOUT(info->xmit.buf + info->xmit.tail, CIRC_CNT(info->xmit.head, @@ -1359,7 +2035,7 @@ #endif if (!info->tr_running) { /* weirdo... we shouldn't get here! */ - printk("Achtung: transmit_chars with !tr_running\n"); + printk(KERN_WARNING "Achtung: transmit_chars_dma with !tr_running\n"); return; } @@ -1375,6 +2051,8 @@ /* otherwise we find the amount of data sent here */ sentl = descr->hw_len; + DFLOW(DEBUG_LOG(info->line, "TX %i done\n", sentl)); + /* update stats */ info->icount.tx += sentl; @@ -1392,6 +2070,13 @@ c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); + /* Don't send all in one DMA transfer - divide it so we wake up + * application before all is sent + */ + + if (c >= 4*WAKEUP_CHARS) + c = c/2; + if (c <= 0) { /* our job here is done, don't schedule any new DMA transfer */ info->tr_running = 0; @@ -1411,17 +2096,17 @@ /* ok we can schedule a dma send of c chars starting at info->xmit.tail */ /* set up the descriptor correctly for output */ - + DFLOW(DEBUG_LOG(info->line, "TX %i\n", c)); descr->ctrl = d_int | d_eol | d_wait; /* Wait needed for tty_wait_until_sent() */ descr->sw_len = c; descr->buf = virt_to_phys(info->xmit.buf + info->xmit.tail); descr->status = 0; *info->ofirstadr = virt_to_phys(descr); /* write to R_DMAx_FIRST */ - *info->ocmdadr = 1; /* dma command start -> R_DMAx_CMD */ + *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start); /* dma command start -> R_DMAx_CMD */ /* DMA is now running (hopefully) */ -} /* transmit_chars */ +} /* transmit_chars_dma */ static void start_transmit(struct e100_serial *info) @@ -1435,15 +2120,17 @@ info->tr_descr.hw_len = 0; info->tr_descr.status = 0; info->tr_running = 1; - - transmit_chars(info); + if (info->uses_dma_out) + transmit_chars_dma(info); + else + e100_enable_serial_tx_ready_irq(info); } /* start_transmit */ #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER static int serial_fast_timer_started = 0; static int serial_fast_timer_expired = 0; static void flush_timeout_function(unsigned long data); -#define START_FLUSH_FAST_TIMER(info, string) {\ +#define START_FLUSH_FAST_TIMER_TIME(info, string, usec) {\ unsigned long timer_flags; \ save_flags(timer_flags); \ cli(); \ @@ -1454,7 +2141,7 @@ start_one_shot_timer(&fast_timers[info->line], \ flush_timeout_function, \ (unsigned long)info, \ - info->char_time_usec*4, \ + (usec), \ string); \ } \ else { \ @@ -1462,8 +2149,10 @@ } \ restore_flags(timer_flags); \ } +#define START_FLUSH_FAST_TIMER(info, string) START_FLUSH_FAST_TIMER_TIME(info, string, info->flush_time_usec) #else +#define START_FLUSH_FAST_TIMER_TIME(info, string, usec) #define START_FLUSH_FAST_TIMER(info, string) #endif @@ -1508,18 +2197,27 @@ add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag) { struct etrax_recv_buffer *buffer; + if (info->uses_dma_in) { + if (!(buffer = alloc_recv_buffer(4))) + return 0; - if (!(buffer = alloc_recv_buffer(4))) - return 0; - - buffer->length = 1; - buffer->error = flag; - buffer->buffer[0] = data; + buffer->length = 1; + buffer->error = flag; + buffer->buffer[0] = data; - append_recv_buffer(info, buffer); - - info->icount.rx++; + append_recv_buffer(info, buffer); + info->icount.rx++; + } else { + struct tty_struct *tty = info->tty; + *tty->flip.char_buf_ptr = data; + *tty->flip.flag_buf_ptr = flag; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + info->icount.rx++; + } + return 1; } @@ -1529,7 +2227,8 @@ struct etrax_recv_buffer *buffer = phys_to_virt(descr->buf) - sizeof *buffer; if (info->recv_cnt + recvl > 65536) { - printk(__FUNCTION__ ": Too much pending incoming serial data! Dropping %u bytes.\n", recvl); + printk(KERN_CRIT + "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __FUNCTION__, recvl); return 0; } @@ -1542,7 +2241,7 @@ append_recv_buffer(info, buffer); if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE))) - panic(__FUNCTION__ ": Failed to allocate memory for receive buffer!\n"); + panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__); descr->buf = virt_to_phys(buffer->buffer); @@ -1579,8 +2278,16 @@ /* Reset the status information */ descr->status = 0; - DEBUG_LOG(info->line, "recvl %lu\n", recvl); - + DFLOW( DEBUG_LOG(info->line, "RX %lu\n", recvl); + if (info->tty->stopped) { + unsigned char *buf = phys_to_virt(descr->buf); + DEBUG_LOG(info->line, "rx 0x%02X\n", buf[0]); + DEBUG_LOG(info->line, "rx 0x%02X\n", buf[1]); + DEBUG_LOG(info->line, "rx 0x%02X\n", buf[2]); + } + ); + + /* update stats */ info->icount.rx += recvl; @@ -1591,7 +2298,7 @@ } static _INLINE_ void -receive_chars(struct e100_serial *info) +receive_chars_dma(struct e100_serial *info) { struct tty_struct *tty; unsigned char rstat; @@ -1623,7 +2330,10 @@ /* Read the status register to detect errors */ rstat = info->port[REG_STATUS]; - + if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) { + DFLOW(DEBUG_LOG(info->line, "XOFF detect stat %x\n", rstat)); + } + if (rstat & SER_ERROR_MASK) { /* If we got an error, we must reset it by reading the * data_in field @@ -1658,7 +2368,7 @@ /* Set up the receiving descriptors */ for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) { if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE))) - panic(__FUNCTION__ ": Failed to allocate memory for receive buffer!\n"); + panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__); descr[i].ctrl = d_int; descr[i].buf = virt_to_phys(buffer->buffer); @@ -1691,20 +2401,20 @@ */ return; #endif + info->tty->flip.count = 0; + if (info->uses_dma_in) { + /* reset the input dma channel to be sure it works */ + + *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); + while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) == + IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); - /* reset the input dma channel to be sure it works */ - - *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) == - IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); - - info->tty->flip.count = 0; - - start_recv_dma(info); + start_recv_dma(info); #ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST - start_flush_timer(); + start_flush_timer(); #endif + } } @@ -1749,25 +2459,25 @@ for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; - if (!info->enabled || !info->uses_dma) + if (!info->enabled || !info->uses_dma_out) continue; /* check for dma_descr (don't need to check for dma_eop in output dma for serial */ if (ireg & info->irq) { /* we can send a new dma bunch. make it so. */ - DEBUG_LOG(info->line, "tr_interrupt %i\n", i); + DINTR2(DEBUG_LOG(info->line, "tr_interrupt %i\n", i)); /* Read jiffies_usec first, * we want this time to be as late as possible */ PROCSTAT(ser_stat[info->line].tx_dma_ints++); info->last_tx_active_usec = GET_JIFFIES_USEC(); info->last_tx_active = jiffies; - transmit_chars(info); + transmit_chars_dma(info); } /* FIXME: here we should really check for a change in the status lines and if so call status_handle(info) */ } -} +} /* tr_interrupt */ /* dma input channel interrupt handler */ @@ -1795,18 +2505,18 @@ for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; - if (!info->enabled || !info->uses_dma) + if (!info->enabled || !info->uses_dma_in) continue; /* check for both dma_eop and dma_descr for the input dma channel */ if (ireg & ((info->irq << 2) | (info->irq << 3))) { /* we have received something */ - receive_chars(info); + receive_chars_dma(info); } /* FIXME: here we should really check for a change in the status lines and if so call status_handle(info) */ } -} +} /* rec_interrupt */ static _INLINE_ int force_eop_if_needed(struct e100_serial *info) @@ -1846,7 +2556,7 @@ if (!info->forced_eop) { info->forced_eop = 1; PROCSTAT(ser_stat[info->line].timeout_flush_cnt++); - DEBUG_LOG(info->line, "timeout EOP %i\n", info->line); + TIMERD(DEBUG_LOG(info->line, "timeout EOP %i\n", info->line)); FORCE_EOP(info); } @@ -1860,7 +2570,8 @@ struct etrax_recv_buffer *buffer; unsigned int length; unsigned long flags; - + int max_flip_size; + if (!info->first_recv_buffer) return; @@ -1873,12 +2584,46 @@ } length = tty->flip.count; + /* Don't flip more than the ldisc has room for. + * The return value from ldisc.receive_room(tty) - might not be up to + * date, the previous flip of up to TTY_FLIPBUF_SIZE might be on the + * processed and not accounted for yet. + * Since we use DMA, 1 SERIAL_DESCR_BUF_SIZE could be on the way. + * Lets buffer data here and let flow control take care of it. + * Since we normally flip large chunks, the ldisc don't react + * with throttle until too late if we flip to much. + */ + max_flip_size = tty->ldisc.receive_room(tty); + if (max_flip_size < 0) + max_flip_size = 0; + if (max_flip_size <= (TTY_FLIPBUF_SIZE + /* Maybe not accounted for */ + length + info->recv_cnt + /* We have this queued */ + 2*SERIAL_DESCR_BUF_SIZE + /* This could be on the way */ + TTY_THRESHOLD_THROTTLE)) { /* Some slack */ + /* check TTY_THROTTLED first so it indicates our state */ + if (!test_and_set_bit(TTY_THROTTLED, &tty->flags)) { + DFLOW(DEBUG_LOG(info->line,"flush_to_flip throttles room %lu\n", max_flip_size)); + rs_throttle(tty); + } +#if 0 + else if (max_flip_size <= (TTY_FLIPBUF_SIZE + /* Maybe not accounted for */ + length + info->recv_cnt + /* We have this queued */ + SERIAL_DESCR_BUF_SIZE + /* This could be on the way */ + TTY_THRESHOLD_THROTTLE)) { /* Some slack */ + DFLOW(DEBUG_LOG(info->line,"flush_to_flip throttles again! %lu\n", max_flip_size)); + rs_throttle(tty); + } +#endif + } - while ((buffer = info->first_recv_buffer) && length < TTY_FLIPBUF_SIZE) { + if (max_flip_size > TTY_FLIPBUF_SIZE) + max_flip_size = TTY_FLIPBUF_SIZE; + + while ((buffer = info->first_recv_buffer) && length < max_flip_size) { unsigned int count = buffer->length; - if (length + count > TTY_FLIPBUF_SIZE) - count = TTY_FLIPBUF_SIZE - length; + if (length + count > max_flip_size) + count = max_flip_size - length; memcpy(tty->flip.char_buf_ptr + length, buffer->buffer, count); memset(tty->flip.flag_buf_ptr + length, TTY_NORMAL, count); @@ -1886,7 +2631,8 @@ length += count; info->recv_cnt -= count; - + DFLIP(DEBUG_LOG(info->line,"flip: %i\n", length)); + if (count == buffer->length) { info->first_recv_buffer = buffer->next; kfree(buffer); @@ -1901,10 +2647,31 @@ info->last_recv_buffer = NULL; tty->flip.count = length; - + DFLIP(if (tty->ldisc.chars_in_buffer(tty) > 3500) { + DEBUG_LOG(info->line, "ldisc %lu\n", + tty->ldisc.chars_in_buffer(tty)); + DEBUG_LOG(info->line, "flip.count %lu\n", + tty->flip.count); + } + ); restore_flags(flags); #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,66) + DFLIP( + if (1) { + + if (test_bit(TTY_DONT_FLIP, &tty->flags)) { + DEBUG_LOG(info->line, "*** TTY_DONT_FLIP set flip.count %i ***\n", tty->flip.count); + DEBUG_LOG(info->line, "*** recv_cnt %i\n", info->recv_cnt); + } else { + } + DEBUG_LOG(info->line, "*** rxtot %i\n", info->icount.rx); + DEBUG_LOG(info->line, "ldisc %lu\n", tty->ldisc.chars_in_buffer(tty)); + DEBUG_LOG(info->line, "room %lu\n", tty->ldisc.receive_room(tty)); + } + + ); + /* this includes a check for low-latency */ tty_flip_buffer_push(tty); #else @@ -1915,12 +2682,19 @@ static _INLINE_ void check_flush_timeout(struct e100_serial *info) { - force_eop_if_needed(info); - + /* Flip what we've got (if we can) */ flush_to_flip_buffer(info); - if (info->first_recv_buffer) - START_FLUSH_FAST_TIMER(info, "flip"); + /* We might need to flip later, but not to fast + * since the system is busy processing input... */ + if (info->first_recv_buffer) + START_FLUSH_FAST_TIMER_TIME(info, "flip", 2000); + + /* Force eop last, since data might have come while we're processing + * and if we started the slow timer above, we won't start a fast + * below. + */ + force_eop_if_needed(info); } #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER @@ -1960,7 +2734,7 @@ for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; - if (info->enabled && info->uses_dma) + if (info->enabled && info->uses_dma_in) check_flush_timeout(info); } } /* timeout_interrupt */ @@ -1988,7 +2762,7 @@ for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; - if (info->uses_dma) + if (info->uses_dma_in) check_flush_timeout(info); } @@ -2017,7 +2791,7 @@ B= Break character (0x00) with framing error. E= Error byte with parity error received after B characters. -F= "Faked" valid byte received immediatly after B characters. +F= "Faked" valid byte received immediately after B characters. V= Valid byte 1. @@ -2054,7 +2828,7 @@ To distinguish a V byte in 1. from an F byte in 2. we keep a timestamp of the last faulty char (B) and compares it with the current time: -If the time elapsed time is less then 2*char_time_usec we will assume +If the time elapsed time is less than 2*char_time_usec we will assume it's a faked F char and not a Valid char and set info->errorcode = ERRCODE_SET_BREAK. @@ -2066,15 +2840,159 @@ TODO: The break will be delayed until an F or V character is received. */ + +extern _INLINE_ +struct e100_serial * handle_ser_rx_interrupt_no_dma(struct e100_serial *info) +{ + unsigned long data_read; + struct tty_struct *tty = info->tty; + + if (!tty) { + printk("!NO TTY!\n"); + return info; + } + if (tty->flip.count >= TTY_FLIPBUF_SIZE - TTY_THRESHOLD_THROTTLE) { + /* check TTY_THROTTLED first so it indicates our state */ + if (!test_and_set_bit(TTY_THROTTLED, &tty->flags)) { + DFLOW(DEBUG_LOG(info->line, "rs_throttle flip.count: %i\n", tty->flip.count)); + rs_throttle(tty); + } + } + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + DEBUG_LOG(info->line, "force FLIP! %i\n", tty->flip.count); + tty->flip.tqueue.routine((void *) tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + DEBUG_LOG(info->line, "FLIP FULL! %i\n", tty->flip.count); + return info; /* if TTY_DONT_FLIP is set */ + } + } + /* Read data and status at the same time */ + data_read = *((unsigned long *)&info->port[REG_DATA_STATUS32]); +more_data: + if (data_read & IO_MASK(R_SERIAL0_READ, xoff_detect) ) { + DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0)); + } + DINTR2(DEBUG_LOG(info->line, "ser_rx %c\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read))); + + if (data_read & ( IO_MASK(R_SERIAL0_READ, framing_err) | + IO_MASK(R_SERIAL0_READ, par_err) | + IO_MASK(R_SERIAL0_READ, overrun) )) { + /* An error */ + info->last_rx_active_usec = GET_JIFFIES_USEC(); + info->last_rx_active = jiffies; + DINTR1(DEBUG_LOG(info->line, "ser_rx err stat_data %04X\n", data_read)); + DLOG_INT_TRIG( + if (!log_int_trig1_pos) { + log_int_trig1_pos = log_int_pos; + log_int(rdpc(), 0, 0); + } + ); + + + if ( ((data_read & IO_MASK(R_SERIAL0_READ, data_in)) == 0) && + (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) ) { + /* Most likely a break, but we get interrupts over and + * over again. + */ -extern void _INLINE_ handle_ser_interrupt(struct e100_serial *info) + if (!info->break_detected_cnt) { + DEBUG_LOG(info->line, "#BRK start\n", 0); + } + if (data_read & IO_MASK(R_SERIAL0_READ, rxd)) { + /* The RX pin is high now, so the break + * must be over, but.... + * we can't really know if we will get another + * last byte ending the break or not. + * And we don't know if the byte (if any) will + * have an error or look valid. + */ + DEBUG_LOG(info->line, "# BL BRK\n", 0); + info->errorcode = ERRCODE_INSERT_BREAK; + } + info->break_detected_cnt++; + } else { + /* The error does not look like a break, but could be + * the end of one + */ + if (info->break_detected_cnt) { + DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt); + info->errorcode = ERRCODE_INSERT_BREAK; + } else { + if (info->errorcode == ERRCODE_INSERT_BREAK) { + info->icount.brk++; + *tty->flip.char_buf_ptr = 0; + *tty->flip.flag_buf_ptr = TTY_BREAK; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + info->icount.rx++; + } + *tty->flip.char_buf_ptr = IO_EXTRACT(R_SERIAL0_READ, data_in, data_read); + + if (data_read & IO_MASK(R_SERIAL0_READ, par_err)) { + info->icount.parity++; + *tty->flip.flag_buf_ptr = TTY_PARITY; + } else if (data_read & IO_MASK(R_SERIAL0_READ, overrun)) { + info->icount.overrun++; + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + } else if (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) { + info->icount.frame++; + *tty->flip.flag_buf_ptr = TTY_FRAME; + } + info->errorcode = 0; + } + info->break_detected_cnt = 0; + } + } else if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) { + /* No error */ + DLOG_INT_TRIG( + if (!log_int_trig1_pos) { + if (log_int_pos >= log_int_size) { + log_int_pos = 0; + } + log_int_trig0_pos = log_int_pos; + log_int(rdpc(), 0, 0); + } + ); + *tty->flip.char_buf_ptr = IO_EXTRACT(R_SERIAL0_READ, data_in, data_read); + *tty->flip.flag_buf_ptr = 0; + } else { + DEBUG_LOG(info->line, "ser_rx int but no data_avail %08lX\n", data_read); + } + + + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + info->icount.rx++; + data_read = *((unsigned long *)&info->port[REG_DATA_STATUS32]); + if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) { + DEBUG_LOG(info->line, "ser_rx %c in loop\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read)); + goto more_data; + } + + tty_flip_buffer_push(info->tty); + return info; +} + +extern _INLINE_ +struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info) { - unsigned char rstat = info->port[REG_STATUS]; + unsigned char rstat; #ifdef SERIAL_DEBUG_INTR printk("Interrupt from serport %d\n", i); #endif /* DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */ + if (!info->uses_dma_in) { + return handle_ser_rx_interrupt_no_dma(info); + } + /* DMA is used */ + rstat = info->port[REG_STATUS]; + if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) { + DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0)); + } + if (rstat & SER_ERROR_MASK) { unsigned char data; @@ -2084,7 +3002,8 @@ * data_in field */ data = info->port[REG_DATA]; - + DINTR1(DEBUG_LOG(info->line, "ser_rx! %c\n", data)); + DINTR1(DEBUG_LOG(info->line, "ser_rx err stat %02X\n", rstat)); if (!data && (rstat & SER_FRAMING_ERR_MASK)) { /* Most likely a break, but we get interrupts over and * over again. @@ -2113,15 +3032,22 @@ DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt); info->errorcode = ERRCODE_INSERT_BREAK; } else { - if (info->errorcode == ERRCODE_INSERT_BREAK) + if (info->errorcode == ERRCODE_INSERT_BREAK) { + info->icount.brk++; add_char_and_flag(info, '\0', TTY_BREAK); + } - if (rstat & SER_PAR_ERR_MASK) + if (rstat & SER_PAR_ERR_MASK) { + info->icount.parity++; add_char_and_flag(info, data, TTY_PARITY); - else if (rstat & SER_OVERRUN_MASK) + } else if (rstat & SER_OVERRUN_MASK) { + info->icount.overrun++; add_char_and_flag(info, data, TTY_OVERRUN); - else if (rstat & SER_FRAMING_ERR_MASK) + } else if (rstat & SER_FRAMING_ERR_MASK) { + info->icount.frame++; add_char_and_flag(info, data, TTY_FRAME); + } + info->errorcode = 0; } info->break_detected_cnt = 0; @@ -2145,7 +3071,7 @@ if (elapsed_usec < 2*info->char_time_usec) { DEBUG_LOG(info->line, "FBRK %i\n", info->line); /* Report as BREAK (error) and let - * receive_chars() handle it + * receive_chars_dma() handle it */ info->errorcode = ERRCODE_SET_BREAK; } else { @@ -2155,38 +3081,195 @@ } #ifdef SERIAL_DEBUG_INTR - printk("** OK, disabling ser_interupts\n"); + printk("** OK, disabling ser_interrupts\n"); #endif e100_disable_serial_data_irq(info); - + DINTR2(DEBUG_LOG(info->line, "ser_rx OK %d\n", info->line)); info->break_detected_cnt = 0; PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++); - DEBUG_LOG(info->line, "ser_int OK %d\n", info->line); } - /* Restarting the DMA never hurts */ *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); START_FLUSH_FAST_TIMER(info, "ser_int"); -} /* handle_ser_interrupt */ + return info; +} /* handle_ser_rx_interrupt */ + +extern _INLINE_ void handle_ser_tx_interrupt(struct e100_serial *info) +{ + unsigned long flags; + + if (info->x_char) { + unsigned char rstat; + DFLOW(DEBUG_LOG(info->line, "tx_int: xchar 0x%02X\n", info->x_char)); + save_flags(flags); cli(); + rstat = info->port[REG_STATUS]; + DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat)); + + info->port[REG_TR_DATA] = info->x_char; + info->icount.tx++; + info->x_char = 0; + /* We must enable since it is disabled in ser_interrupt */ + e100_enable_serial_tx_ready_irq(info); + restore_flags(flags); + return; + } + if (info->uses_dma_out) { + unsigned char rstat; + int i; + /* We only use normal tx interrupt when sending x_char */ + DFLOW(DEBUG_LOG(info->line, "tx_int: xchar sent\n", 0)); + save_flags(flags); cli(); + rstat = info->port[REG_STATUS]; + DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat)); + e100_disable_serial_tx_ready_irq(info); + if (info->tty->stopped) + rs_stop(info->tty); + /* Enable the DMA channel and tell it to continue */ + e100_enable_txdma_channel(info); + /* Wait 12 cycles before doing the DMA command */ + for(i = 6; i > 0; i--) + nop(); + + *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, continue); + restore_flags(flags); + return; + } + /* Normal char-by-char interrupt */ + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n", info->tty->stopped)); + e100_disable_serial_tx_ready_irq(info); + info->tr_running = 0; + return; + } + DINTR2(DEBUG_LOG(info->line, "tx_int %c\n", info->xmit.buf[info->xmit.tail])); + /* Send a byte, rs485 timing is critical so turn of ints */ + save_flags(flags); cli(); + info->port[REG_TR_DATA] = info->xmit.buf[info->xmit.tail]; + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->icount.tx++; + if (info->xmit.head == info->xmit.tail) { +#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER) + if (info->rs485.enabled) { + /* Set a short timer to toggle RTS */ + start_one_shot_timer(&fast_timers_rs485[info->line], + rs485_toggle_rts_timer_function, + (unsigned long)info, + info->char_time_usec*2, + "RS-485"); + } +#endif /* RS485 */ + info->last_tx_active_usec = GET_JIFFIES_USEC(); + info->last_tx_active = jiffies; + e100_disable_serial_tx_ready_irq(info); + DFLOW(DEBUG_LOG(info->line, "tx_int: stop2\n", 0)); + } else { + /* We must enable since it is disabled in ser_interrupt */ + e100_enable_serial_tx_ready_irq(info); + } + restore_flags(flags); + + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < WAKEUP_CHARS) + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); +} /* handle_ser_tx_interrupt */ + +/* result of time measurements: + * RX duration 54-60 us when doing something, otherwise 6-9 us + * ser_int duration: just sending: 8-15 us normally, up to 73 us + */ static void ser_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + static volatile int tx_started = 0; struct e100_serial *info; int i; - + unsigned long flags; + unsigned long irq_mask1_rd; + unsigned long data_mask = (1 << (8+2*0)); /* ser0 data_avail */ + static volatile unsigned long reentered_ready_mask = 0; + + save_flags(flags); cli(); + irq_mask1_rd = *R_IRQ_MASK1_RD; + /* First handle all rx interrupts with ints disabled */ + info = rs_table; + irq_mask1_rd &= e100_ser_int_mask; for (i = 0; i < NR_PORTS; i++) { - info = rs_table + i; - - if (!info->enabled || !info->uses_dma) - continue; - - /* Which line caused the irq? */ - if (*R_IRQ_MASK1_RD & (1U << (8+2*info->line))) { - handle_ser_interrupt(info); + /* Which line caused the data irq? */ + if (irq_mask1_rd & data_mask) { + handle_ser_rx_interrupt(info); } + info += 1; + data_mask <<= 2; } + /* Handle tx interrupts with interrupts enabled so we + * can take care of new data interrupts while transmitting + * We protect the tx part with the tx_started flag. + * We disable the tr_ready interrupts we are about to handle and + * unblock the serial interrupt so new serial interrupts may come. + * + * If we get a new interrupt: + * - it migth be due to synchronous serial ports. + * - serial irq will be blocked by general irq handler. + * - async data will be handled above (sync will be ignored). + * - tx_started flag will prevent us from trying to send again and + * we will exit fast - no need to unblock serial irq. + * - Next (sync) serial interrupt handler will be runned with + * disabled interrupt due to restore_flags() at end of function, + * so sync handler will not be preempted or reentered. + */ + if (!tx_started) { + unsigned long ready_mask; + unsigned long + tx_started = 1; + /* Only the tr_ready interrupts left */ + irq_mask1_rd &= (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) | + IO_MASK(R_IRQ_MASK1_RD, ser1_ready) | + IO_MASK(R_IRQ_MASK1_RD, ser2_ready) | + IO_MASK(R_IRQ_MASK1_RD, ser3_ready)); + while (irq_mask1_rd) { + /* Disable those we are about to handle */ + *R_IRQ_MASK1_CLR = irq_mask1_rd; + /* Unblock the serial interrupt */ + *R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set); + + sti(); + ready_mask = (1 << (8+1+2*0)); /* ser0 tr_ready */ + info = rs_table; + for (i = 0; i < NR_PORTS; i++) { + /* Which line caused the ready irq? */ + if (irq_mask1_rd & ready_mask) { + handle_ser_tx_interrupt(info); + } + info += 1; + ready_mask <<= 2; + } + /* handle_ser_tx_interrupt enables tr_ready interrupts */ + cli(); + /* Handle reentered TX interrupt */ + irq_mask1_rd = reentered_ready_mask; + } + cli(); + tx_started = 0; + } else { + unsigned long ready_mask; + ready_mask = irq_mask1_rd & (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) | + IO_MASK(R_IRQ_MASK1_RD, ser1_ready) | + IO_MASK(R_IRQ_MASK1_RD, ser2_ready) | + IO_MASK(R_IRQ_MASK1_RD, ser3_ready)); + if (ready_mask) { + reentered_ready_mask |= ready_mask; + /* Disable those we are about to handle */ + *R_IRQ_MASK1_CLR = ready_mask; + DFLOW(DEBUG_LOG(SERIAL_DEBUG_LINE, "ser_int reentered with TX %X\n", ready_mask)); + } + } + + restore_flags(flags); } /* ser_interrupt */ #endif @@ -2288,25 +3371,40 @@ * Reset the DMA channels and make sure their interrupts are cleared */ - info->uses_dma = 1; - *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - - /* Wait until reset cycle is complete */ - while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) == - IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); - - while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) == - IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); + if (info->dma_in_enabled) { + info->uses_dma_in = 1; + e100_enable_rxdma_channel(info); + + *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - /* Make sure the irqs are cleared */ - *info->iclrintradr = - IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | - IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); - *info->oclrintradr = - IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | - IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); + /* Wait until reset cycle is complete */ + while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) == + IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); + + /* Make sure the irqs are cleared */ + *info->iclrintradr = + IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | + IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); + } else { + e100_disable_rxdma_channel(info); + } + + if (info->dma_out_enabled) { + info->uses_dma_out = 1; + e100_enable_txdma_channel(info); + *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); + + while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) == + IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); + /* Make sure the irqs are cleared */ + *info->oclrintradr = + IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | + IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); + } else { + e100_disable_txdma_channel(info); + } + if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); @@ -2321,8 +3419,10 @@ * and set the speed and other flags of the serial port * this will start the rx/tx as well */ + #ifdef SERIAL_HANDLE_EARLY_ERRORS - e100_enable_serial_data_irq(info); + if (info->uses_dma_in) + e100_enable_serial_data_irq(info); #endif change_speed(info); @@ -2331,9 +3431,10 @@ (void)info->port[REG_DATA]; /* enable the interrupts */ + if (info->uses_dma_out) + e100_enable_txdma_irq(info); - e100_enable_txdma_irq(info); - e100_enable_rxdma_irq(info); + e100_enable_rx_irq(info); info->tr_running = 0; /* to be sure we don't lock up the transmitter */ @@ -2342,10 +3443,10 @@ start_receive(info); /* for safety, make sure the descriptors last result is 0 bytes written */ - info->tr_descr.sw_len = 0; info->tr_descr.hw_len = 0; info->tr_descr.status = 0; + /* enable RTS/DTR last */ @@ -2374,20 +3475,28 @@ #ifndef CONFIG_SVINTO_SIM /* shut down the transmitter and receiver */ - + DFLOW(DEBUG_LOG(info->line, "shutdown %i\n", info->line)); e100_disable_rx(info); info->port[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40); - e100_disable_rxdma_irq(info); - e100_disable_txdma_irq(info); - - info->tr_running = 0; - - /* reset both dma channels */ - - *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - info->uses_dma = 0; + /* disable interrupts, reset dma channels */ + if (info->uses_dma_in) { + e100_disable_rxdma_irq(info); + *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); + info->uses_dma_in = 0; + } else { + e100_disable_serial_data_irq(info); + } + + if (info->uses_dma_out) { + e100_disable_txdma_irq(info); + info->tr_running = 0; + *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); + info->uses_dma_out = 0; + } else { + e100_disable_serial_tx_ready_irq(info); + info->tr_running = 0; + } #endif /* CONFIG_SVINTO_SIM */ @@ -2435,7 +3544,7 @@ { unsigned int cflag; unsigned long xoff; - + unsigned long flags; /* first some safety checks */ if (!info->tty || !info->tty->termios) @@ -2444,17 +3553,80 @@ return; cflag = info->tty->termios->c_cflag; - + /* possibly, the tx/rx should be disabled first to do this safely */ /* change baud-rate and write it to the hardware */ - - info->baud = cflag_to_baud(cflag); + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { + /* Special baudrate */ + u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */ + unsigned long alt_source = + IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) | + IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal); + /* R_ALT_SER_BAUDRATE selects the source */ + DBAUD(printk("Custom baudrate: baud_base/divisor %lu/%i\n", + (unsigned long)info->baud_base, info->custom_divisor)); + if (info->baud_base == SERIAL_PRESCALE_BASE) { + /* 0, 2-65535 (0=65536) */ + u16 divisor = info->custom_divisor; + /* R_SERIAL_PRESCALE (upper 16 bits of R_CLOCK_PRESCALE) */ + /* baudrate is 3.125MHz/custom_divisor */ + alt_source = + IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, prescale) | + IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, prescale); + alt_source = 0x11; + DBAUD(printk("Writing SERIAL_PRESCALE: divisor %i\n", divisor)); + *R_SERIAL_PRESCALE = divisor; + info->baud = SERIAL_PRESCALE_BASE/divisor; + } +#ifdef CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED + else if ((info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8 && + info->custom_divisor == 1) || + (info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ && + info->custom_divisor == 8)) { + /* ext_clk selected */ + alt_source = + IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, extern) | + IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, extern); + DBAUD(printk("using external baudrate: %lu\n", CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8)); + info->baud = CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8; + } + } +#endif + else + { + /* Bad baudbase, we don't support using timer0 + * for baudrate. + */ + printk(KERN_WARNING "Bad baud_base/custom_divisor: %lu/%i\n", + (unsigned long)info->baud_base, info->custom_divisor); + } + r_alt_ser_baudrate_shadow &= ~mask; + r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8)); + *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow; + } else { + /* Normal baudrate */ + /* Make sure we use normal baudrate */ + u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */ + unsigned long alt_source = + IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) | + IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal); + r_alt_ser_baudrate_shadow &= ~mask; + r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8)); +#ifndef CONFIG_SVINTO_SIM + *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow; +#endif /* CONFIG_SVINTO_SIM */ + + info->baud = cflag_to_baud(cflag); +#ifndef CONFIG_SVINTO_SIM + info->port[REG_BAUD] = cflag_to_etrax_baud(cflag); +#endif /* CONFIG_SVINTO_SIM */ + } #ifndef CONFIG_SVINTO_SIM - info->port[REG_BAUD] = cflag_to_etrax_baud(cflag); /* start with default settings and then fill in changes */ - + save_flags(flags); + cli(); /* 8 bit, no/even parity */ info->rx_ctrl &= ~(IO_MASK(R_SERIAL0_REC_CTRL, rec_bitnr) | IO_MASK(R_SERIAL0_REC_CTRL, rec_par_en) | @@ -2484,14 +3656,26 @@ info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable); } - if (cflag & PARODD) { - /* set odd parity */ - info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd); - info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd); + if (cflag & CMSPAR) { + /* enable stick parity */ + info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_stick_par, stick); + info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, stick); + if (!(cflag & PARODD)) { + /* set mark parity */ + info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd); + info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd); + } + } else { + if (cflag & PARODD) { + /* set odd parity */ + info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd); + info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd); + } } if (cflag & CRTSCTS) { /* enable automatic CTS handling */ + DFLOW(DEBUG_LOG(info->line, "FLOW auto_cts enabled\n", 0)); info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, active); } @@ -2507,10 +3691,12 @@ xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->tty)); xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable); if (info->tty->termios->c_iflag & IXON ) { + DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n", STOP_CHAR(info->tty))); xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); } *((unsigned long *)&info->port[REG_XOFF]) = xoff; + restore_flags(flags); #endif /* !CONFIG_SVINTO_SIM */ update_char_time(info); @@ -2543,8 +3729,8 @@ restore_flags(flags); } -extern inline int -raw_write(struct tty_struct * tty, int from_user, +extern _INLINE_ int +rs_raw_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { int c, ret = 0; @@ -2558,7 +3744,7 @@ #ifdef SERIAL_DEBUG_DATA if (info->line == SERIAL_DEBUG_LINE) - printk("raw_write (%d), status %d\n", + printk("rs_raw_write (%d), status %d\n", count, info->port[REG_STATUS]); #endif @@ -2568,6 +3754,9 @@ return count; #endif save_flags(flags); + + DFLOW(DEBUG_LOG(info->line, "write count %i ", count)); + DFLOW(DEBUG_LOG(info->line, "ldisc %i\n", tty->ldisc.chars_in_buffer(tty))); /* the cli/restore_flags pairs below are needed because the * DMA interrupt handler moves the info->xmit values. the memcpy @@ -2635,6 +3824,7 @@ * this does not need IRQ protection since if tr_running == 0 * the IRQ's are not running anyway for this port. */ + DFLOW(DEBUG_LOG(info->line, "write ret %i\n", ret)); if (info->xmit.head != info->xmit.tail && !tty->stopped && @@ -2644,7 +3834,7 @@ } return ret; -} /* raw_write() */ +} /* rs_raw_write() */ static int rs_write(struct tty_struct * tty, int from_user, @@ -2666,7 +3856,7 @@ e100_rts(info, info->rs485.rts_on_send); #if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) e100_disable_rx(info); - e100_disable_rxdma_irq(info); + e100_disable_rx_irq(info); #endif if (info->rs485.delay_rts_before_send > 0) { @@ -2676,7 +3866,7 @@ } #endif /* CONFIG_ETRAX_RS485 */ - count = raw_write(tty, from_user, buf, count); + count = rs_raw_write(tty, from_user, buf, count); #if defined(CONFIG_ETRAX_RS485) if (info->rs485.enabled) @@ -2704,7 +3894,7 @@ #if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) e100_enable_rx(info); - e100_enable_rxdma_irq(info); + e100_enable_rx_irq(info); #endif } #endif /* CONFIG_ETRAX_RS485 */ @@ -2760,20 +3950,33 @@ * This function is used to send a high-priority XON/XOFF character to * the device * - * Since we don't bother to check for info->x_char in transmit_chars yet, - * we don't really implement this function yet. + * Since we use DMA we don't check for info->x_char in transmit_chars_dma(), + * but we do it in handle_ser_tx_interrupt(). + * We disable DMA channel and enable tx ready interrupt and write the + * character when possible. */ static void rs_send_xchar(struct tty_struct *tty, char ch) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + save_flags(flags); cli(); + if (info->uses_dma_out) { + /* Put the DMA on hold and disable the channel */ + *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, hold); + while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) != + IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, hold)); + e100_disable_txdma_channel(info); + } + + /* Must make sure transmitter is not stopped before we can transmit */ + if (tty->stopped) + rs_start(tty); - printk("serial.c:rs_send_xchar not implemented!\n"); - + /* Enable manual transmit interrupt and send from there */ + DFLOW(DEBUG_LOG(info->line, "rs_send_xchar 0x%02X\n", ch)); info->x_char = ch; - if (ch) { - /* Make sure transmit interrupts are on */ - /* TODO. */ - } + e100_enable_serial_tx_ready_irq(info); + restore_flags(flags); } /* @@ -2788,50 +3991,49 @@ rs_throttle(struct tty_struct * tty) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; - unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - printk("throttle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); + printk("throttle %s: %lu....\n", tty_name(tty, buf), + (unsigned long)tty->ldisc.chars_in_buffer(tty)); #endif - + DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty))); + + /* Do RTS before XOFF since XOFF might take some time */ + if (tty->termios->c_cflag & CRTSCTS) { + /* Turn off RTS line */ + e100_rts(info, 0); + } if (I_IXOFF(tty)) - info->x_char = STOP_CHAR(tty); + rs_send_xchar(tty, STOP_CHAR(tty)); - /* Turn off RTS line (do this atomic) should here be an else ?? */ - - save_flags(flags); - cli(); - e100_rts(info, 0); - restore_flags(flags); } static void rs_unthrottle(struct tty_struct * tty) { struct e100_serial *info = (struct e100_serial *)tty->driver_data; - unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - printk("unthrottle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); + printk("unthrottle %s: %lu....\n", tty_name(tty, buf), + (unsigned long)tty->ldisc.chars_in_buffer(tty)); #endif + DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty))); + DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count)); + /* Do RTS before XOFF since XOFF might take some time */ + if (tty->termios->c_cflag & CRTSCTS) { + /* Assert RTS line */ + e100_rts(info, 1); + } if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else - info->x_char = START_CHAR(tty); + rs_send_xchar(tty, START_CHAR(tty)); } - /* Assert RTS line (do this atomic) */ - - save_flags(flags); - cli(); - e100_rts(info, 1); - restore_flags(flags); } /* @@ -2859,8 +4061,10 @@ tmp.port = (int)info->port; tmp.irq = info->irq; tmp.flags = info->flags; + tmp.baud_base = info->baud_base; tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; + tmp.custom_divisor = info->custom_divisor; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; return 0; @@ -2897,9 +4101,11 @@ * OK, past this point, all the error checking has been done. * At this point, we start making changes..... */ - + + info->baud_base = new_serial.baud_base; info->flags = ((info->flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); + info->custom_divisor = new_serial.custom_divisor; info->type = new_serial.type; info->close_delay = new_serial.close_delay; info->closing_wait = new_serial.closing_wait; @@ -3098,18 +4304,20 @@ info->tx_ctrl &= 0x3F; /* Clear bit 7 (txd) and 6 (tr_enable) */ info->port[REG_TR_CTRL] = info->tx_ctrl; - + restore_flags(flags); /* wait for "duration" jiffies */ schedule(); + cli(); info->tx_ctrl |= (0x80 | 0x40); /* Set bit 7 (txd) and 6 (tr_enable) */ info->port[REG_TR_CTRL] = info->tx_ctrl; /* the DMA gets awfully confused if we toggle the tranceiver like this * so we need to reset it */ - *info->ocmdadr = 4; + if (info->uses_dma_out) + *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); restore_flags(flags); } @@ -3252,6 +4460,7 @@ change_speed(info); + /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; @@ -3301,12 +4510,13 @@ * one, we've got real problems, since it means the * serial port won't be shutdown. */ - printk("rs_close: bad serial port count; tty->count is 1, " + printk(KERN_CRIT + "rs_close: bad serial port count; tty->count is 1, " "info->count is %d\n", info->count); info->count = 1; } if (--info->count < 0) { - printk("rs_close: bad serial port count for ttyS%d: %d\n", + printk(KERN_CRIT "rs_close: bad serial port count for ttyS%d: %d\n", info->line, info->count); info->count = 0; } @@ -3340,7 +4550,7 @@ #ifndef CONFIG_SVINTO_SIM e100_disable_rx(info); - e100_disable_rxdma_irq(info); + e100_disable_rx_irq(info); if (info->flags & ASYNC_INITIALIZED) { /* @@ -3676,6 +4886,12 @@ #ifdef SERIAL_DEBUG_OPEN printk("rs_open ttyS%d successful...\n", info->line); #endif + DLOG_INT_TRIG( log_int_pos = 0); + + DFLIP( if (info->line == SERIAL_DEBUG_LINE) { + info->icount.rx = 0; + } ); + return 0; } @@ -3683,11 +4899,12 @@ * /proc fs routines.... */ -extern inline int line_info(char *buf, struct e100_serial *info) +extern _INLINE_ int line_info(char *buf, struct e100_serial *info) { char stat_buf[30]; int ret; - + unsigned long tmp; + ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d", info->line, (unsigned long)info->port, info->irq); @@ -3716,11 +4933,39 @@ ret += sprintf(buf+ret, " tx:%lu rx:%lu", (unsigned long)info->icount.tx, (unsigned long)info->icount.rx); - + tmp = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); + if (tmp) { + ret += sprintf(buf+ret, " tx_pend:%lu/%lu", + (unsigned long)tmp, + (unsigned long)SERIAL_XMIT_SIZE); + } + ret += sprintf(buf+ret, " rx_pend:%lu/%lu", (unsigned long)info->recv_cnt, (unsigned long)info->max_recv_cnt); +#if 1 + if (info->tty) { + + if (info->tty->stopped) + ret += sprintf(buf+ret, " stopped:%i", + (int)info->tty->stopped); + if (info->tty->hw_stopped) + ret += sprintf(buf+ret, " hw_stopped:%i", + (int)info->tty->hw_stopped); + } + + { + unsigned char rstat = info->port[REG_STATUS]; + if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) + ret += sprintf(buf+ret, " xoff_detect:1"); + } + +#endif + + + + if (info->icount.frame) ret += sprintf(buf+ret, " fe:%lu", (unsigned long)info->icount.frame); @@ -3744,12 +4989,14 @@ return ret; } + + int rs_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { int i, len = 0, l; off_t begin = 0; - + len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); for (i = 0; i < NR_PORTS && len < 4000; i++) { @@ -3764,6 +5011,22 @@ len = 0; } } +#ifdef DEBUG_LOG_INCLUDED + for (i = 0; i < debug_log_pos; i++) { + len += sprintf(page + len, "%-4i %lu.%lu ", i, debug_log[i].time, timer_data_to_ns(debug_log[i].timer_data)); + len += sprintf(page + len, debug_log[i].string, debug_log[i].value); + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + len += sprintf(page + len, "debug_log %i/%i %li bytes\n", + i, DEBUG_LOG_SIZE, begin+len); + debug_log_pos = 0; +#endif + *eof = 1; done: if (off >= len+begin) @@ -3777,7 +5040,8 @@ static void show_serial_version(void) { - printk("ETRAX 100LX serial-driver %s, (c) 2000-2002 Axis Communications AB\r\n", + printk(KERN_INFO + "ETRAX 100LX serial-driver %s, (c) 2000-2003 Axis Communications AB\r\n", &serial_version[11]); /* "$Revision: x.yy" */ } @@ -3794,7 +5058,7 @@ init_bh(SERIAL_BH, do_serial_bh); /* Setup the timed flush handler system */ - + #if !defined(CONFIG_ETRAX_SERIAL_FAST_TIMER) && !defined(CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST) init_timer(&flush_timer); flush_timer.function = timed_flush_handler; @@ -3868,12 +5132,15 @@ /* do some initializing for the separate ports */ for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) { - info->uses_dma = 0; + info->uses_dma_in = 0; + info->uses_dma_out = 0; info->line = i; info->tty = 0; info->type = PORT_ETRAX; info->tr_running = 0; info->forced_eop = 0; + info->baud_base = DEF_BAUD_BASE; + info->custom_divisor = 0; info->flags = 0; info->close_delay = 5*HZ/10; info->closing_wait = 30*HZ; @@ -3920,41 +5187,58 @@ #ifndef CONFIG_SVINTO_SIM /* Not needed in simulator. May only complicate stuff. */ /* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */ + + if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial ", NULL)) + panic("irq8"); + #ifdef CONFIG_ETRAX_SERIAL_PORT0 +#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT if (request_irq(SER0_DMA_TX_IRQ_NBR, tr_interrupt, SA_INTERRUPT, "serial 0 dma tr", NULL)) panic("irq22"); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN if (request_irq(SER0_DMA_RX_IRQ_NBR, rec_interrupt, SA_INTERRUPT, "serial 0 dma rec", NULL)) panic("irq23"); #endif -#ifdef SERIAL_HANDLE_EARLY_ERRORS - if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial ", NULL)) - panic("irq8"); #endif + #ifdef CONFIG_ETRAX_SERIAL_PORT1 +#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT if (request_irq(SER1_DMA_TX_IRQ_NBR, tr_interrupt, SA_INTERRUPT, "serial 1 dma tr", NULL)) panic("irq24"); +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN if (request_irq(SER1_DMA_RX_IRQ_NBR, rec_interrupt, SA_INTERRUPT, "serial 1 dma rec", NULL)) panic("irq25"); #endif +#endif #ifdef CONFIG_ETRAX_SERIAL_PORT2 /* DMA Shared with par0 (and SCSI0 and ATA) */ - if (request_irq(SER2_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ, "serial 2 dma tr", NULL)) +#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT + if (request_irq(SER2_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 2 dma tr", NULL)) panic("irq18"); - if (request_irq(SER2_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ, "serial 2 dma rec", NULL)) +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN + if (request_irq(SER2_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 2 dma rec", NULL)) panic("irq19"); #endif +#endif #ifdef CONFIG_ETRAX_SERIAL_PORT3 /* DMA Shared with par1 (and SCSI1 and Extern DMA 0) */ - if (request_irq(SER3_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ, "serial 3 dma tr", NULL)) +#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT + if (request_irq(SER3_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 3 dma tr", NULL)) panic("irq20"); - if (request_irq(SER3_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ, "serial 3 dma rec", NULL)) +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN + if (request_irq(SER3_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 3 dma rec", NULL)) panic("irq21"); #endif +#endif #ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST if (request_irq(TIMER1_IRQ_NBR, timeout_interrupt, SA_SHIRQ | SA_INTERRUPT, "fast serial dma timeout", NULL)) { - printk("err: timer1 irq\n"); + printk(KERN_CRIT "err: timer1 irq\n"); } #endif #endif /* CONFIG_SVINTO_SIM */ diff -urN linux-2.4.21-bk5/arch/cris/drivers/serial.h linux-2.4.21-bk6/arch/cris/drivers/serial.h --- linux-2.4.21-bk5/arch/cris/drivers/serial.h 2002-08-02 17:39:42.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/serial.h 2003-07-10 02:54:58.000000000 -0700 @@ -23,7 +23,7 @@ * For definitions of the flags field, see tty.h */ -#define SERIAL_RECV_DESCRIPTORS 8 +#define SERIAL_RECV_DESCRIPTORS 16 struct etrax_recv_buffer { struct etrax_recv_buffer *next; @@ -44,30 +44,29 @@ volatile u32 *ofirstadr; /* adr to R_DMA_CHx_FIRST */ volatile u8 *ocmdadr; /* adr to R_DMA_CHx_CMD */ const volatile u8 *ostatusadr; /* adr to R_DMA_CHx_STATUS */ - volatile u32 *ohwswadr; /* adr to R_DMA_CHx_HWSW */ - volatile u32 *odescradr; /* adr to R_DMA_CHx_DESCR */ /* Input registers */ volatile u8 *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */ volatile u32 *ifirstadr; /* adr to R_DMA_CHx_FIRST */ volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD */ - const volatile u8 *istatusadr; /* adr to R_DMA_CHx_STATUS */ - volatile u32 *ihwswadr; /* adr to R_DMA_CHx_HWSW */ volatile u32 *idescradr; /* adr to R_DMA_CHx_DESCR */ int flags; /* defined in tty.h */ - u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */ - u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */ - u8 iseteop; /* bit number for R_SET_EOP for the input dma */ - - int enabled; /* Set to 1 if the port is enabled in HW config */ - + u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */ + u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */ + u8 iseteop; /* bit number for R_SET_EOP for the input dma */ + u8 enabled; /* Set to 1 if the port is enabled in HW config */ + + u8 dma_out_enabled:1; /* Set to 1 if DMA should be used */ + u8 dma_in_enabled:1; /* Set to 1 if DMA should be used */ + /* end of fields defined in rs_table[] in .c-file */ - - int uses_dma; /* Set to 1 if DMA should be used */ - unsigned char forced_eop; /* a fifo eop has been forced */ - + u8 uses_dma_in; /* Set to 1 if DMA is used */ + u8 uses_dma_out; /* Set to 1 if DMA is used */ + u8 forced_eop; /* a fifo eop has been forced */ + int baud_base; /* For special baudrates */ + int custom_divisor; /* For special baudrates */ struct etrax_dma_descr tr_descr; struct etrax_dma_descr rec_descr[SERIAL_RECV_DESCRIPTORS]; int cur_rec_descr; @@ -108,6 +107,7 @@ #endif unsigned long char_time_usec; /* The time for 1 char, in usecs */ + unsigned long flush_time_usec; /* How often we should flush */ unsigned long last_tx_active_usec; /* Last tx usec in the jiffies */ unsigned long last_tx_active; /* Last tx time in jiffies */ unsigned long last_rx_active_usec; /* Last rx usec in the jiffies */ diff -urN linux-2.4.21-bk5/arch/cris/drivers/sync_serial.c linux-2.4.21-bk6/arch/cris/drivers/sync_serial.c --- linux-2.4.21-bk5/arch/cris/drivers/sync_serial.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/sync_serial.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,7 +1,7 @@ /* * Simple synchronous serial port driver for ETRAX 100LX. * - * Synchronous serial ports are used for continous streamed data like audio. + * Synchronous serial ports are used for continuous streamed data like audio. * The default setting for this driver is compatible with the STA 013 MP3 * decoder. The driver can easily be tuned to fit other audio encoder/decoders * and SPI @@ -29,7 +29,7 @@ #include #include -/* The receiver is a bit tricky beacuse of the continous stream of data. */ +/* The receiver is a bit tricky beacuse of the continuous stream of data.*/ /* */ /* Two DMA descriptors are linked together. Each DMA descriptor is */ /* responsible for one half of a common buffer. */ @@ -256,7 +256,7 @@ initialize_port(1); if (ports[0].use_dma) /* Port 0 uses dma, we must manual allocate IRQ */ { - if (request_irq(8, manual_interrupt, SA_SHIRQ | SA_INTERRUPT, "synchronous serial manual irq", &ports[1])) + if (request_irq(8, manual_interrupt, SA_SHIRQ | SA_INTERRUPT, "synchronous serial manual irq", &ports[1])) panic("Can't allocate sync serial manual irq"); } #endif diff -urN linux-2.4.21-bk5/arch/cris/drivers/usb-host.c linux-2.4.21-bk6/arch/cris/drivers/usb-host.c --- linux-2.4.21-bk5/arch/cris/drivers/usb-host.c 2002-11-28 15:53:09.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/drivers/usb-host.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,10 +1,7 @@ /* * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD) * - * Copyright (c) 2001 Axis Communications AB. - * - * $Id: usb-host.c,v 1.13 2001/11/13 12:06:17 pkj Exp $ - * + * Copyright (c) 2002, 2003 Axis Communications AB. */ #include @@ -19,6 +16,7 @@ #include #include #include +#include #include #include @@ -28,24 +26,37 @@ #include #include +/* Ugly include because we don't live with the other host drivers. */ +#include <../drivers/usb/hcd.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 20) +typedef struct urb urb_t, *purb_t; +typedef struct iso_packet_descriptor iso_packet_descriptor_t; +typedef struct usb_ctrlrequest devrequest; +#endif + #include "usb-host.h" #define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR #define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR #define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR -static const char *usb_hcd_version = "$Revision: 1.13 $"; +static const char *usb_hcd_version = "$Revision: 1.18 $"; #undef KERN_DEBUG #define KERN_DEBUG "" + #undef USB_DEBUG_RH -#undef USB_DEBUG_EP +#undef USB_DEBUG_EPID +#undef USB_DEBUG_SB #undef USB_DEBUG_DESC +#undef USB_DEBUG_URB #undef USB_DEBUG_TRACE -#undef USB_DEBUG_CTRL #undef USB_DEBUG_BULK +#undef USB_DEBUG_CTRL #undef USB_DEBUG_INTR +#undef USB_DEBUG_ISOC #ifdef USB_DEBUG_RH #define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg) @@ -53,10 +64,16 @@ #define dbg_rh(format, arg...) do {} while (0) #endif -#ifdef USB_DEBUG_EP -#define dbg_ep(format, arg...) printk(KERN_DEBUG __FILE__ ": (EP) " format "\n" , ## arg) +#ifdef USB_DEBUG_EPID +#define dbg_epid(format, arg...) printk(KERN_DEBUG __FILE__ ": (EPID) " format "\n" , ## arg) #else -#define dbg_ep(format, arg...) do {} while (0) +#define dbg_epid(format, arg...) do {} while (0) +#endif + +#ifdef USB_DEBUG_SB +#define dbg_sb(format, arg...) printk(KERN_DEBUG __FILE__ ": (SB) " format "\n" , ## arg) +#else +#define dbg_sb(format, arg...) do {} while (0) #endif #ifdef USB_DEBUG_CTRL @@ -77,6 +94,12 @@ #define dbg_intr(format, arg...) do {} while (0) #endif +#ifdef USB_DEBUG_ISOC +#define dbg_isoc(format, arg...) printk(KERN_DEBUG __FILE__ ": (ISOC) " format "\n" , ## arg) +#else +#define dbg_isoc(format, arg...) do {} while (0) +#endif + #ifdef USB_DEBUG_TRACE #define DBFENTER (printk(KERN_DEBUG __FILE__ ": Entering: " __FUNCTION__ "\n")) #define DBFEXIT (printk(KERN_DEBUG __FILE__ ": Exiting: " __FUNCTION__ "\n")) @@ -85,6 +108,7 @@ #define DBFEXIT do {} while (0) #endif + /*------------------------------------------------------------------- Virtual Root Hub -------------------------------------------------------------------*/ @@ -158,78 +182,262 @@ 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ }; +static struct timer_list bulk_start_timer; +static struct timer_list bulk_eot_timer; +/* We want the start timer to expire before the eot timer, because the former might start + traffic, thus making it unnecessary for the latter to time out. */ +#define BULK_START_TIMER_INTERVAL (HZ/10) /* 100 ms */ +#define BULK_EOT_TIMER_INTERVAL (HZ/10+2) /* 120 ms */ #define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break #define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \ {panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);} -static unsigned long submit_urb_count = 0; +#define SLAB_FLAG (in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL) +#define KMALLOC_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) + +/* Most helpful debugging aid */ +#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) -//#define ETRAX_USB_INTR_IRQ -//#define ETRAX_USB_INTR_ERROR_FATAL +/* Alternative assert define which stops after a failed assert. */ +/* +#define assert(expr) \ +{ \ + if (!(expr)) { \ + err("assert failed at line %d",__LINE__); \ + while (1); \ + } \ +} +*/ -#define RX_BUF_SIZE 32768 -#define RX_DESC_BUF_SIZE 64 -#define NBR_OF_RX_DESC (RX_BUF_SIZE / RX_DESC_BUF_SIZE) -#define NBR_OF_EP_DESC 32 +/* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it dynamically? + To adjust it dynamically we would have to get an interrupt when we reach the end + of the rx descriptor list, or when we get close to the end, and then allocate more + descriptors. */ + +#define NBR_OF_RX_DESC 512 +#define RX_DESC_BUF_SIZE 1024 +#define RX_BUF_SIZE (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE) + +/* The number of epids is, among other things, used for pre-allocating + ctrl, bulk and isoc EP descriptors (one for each epid). + Assumed to be > 1 when initiating the DMA lists. */ +#define NBR_OF_EPIDS 32 +/* Support interrupt traffic intervals up to 128 ms. */ #define MAX_INTR_INTERVAL 128 -static __u32 ep_usage_bitmask; -static __u32 ep_really_active; -static __u32 ep_out_traffic; - -static unsigned char RxBuf[RX_BUF_SIZE]; -static USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4))); +/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table + must be "invalid". By this we mean that we shouldn't care about epid attentions + for this epid, or at least handle them differently from epid attentions for "valid" + epids. This define determines which one to use (don't change it). */ +#define INVALID_EPID 31 +/* A special epid for the bulk dummys. */ +#define DUMMY_EPID 30 + +/* This is just a software cache for the valid entries in R_USB_EPT_DATA. */ +static __u32 epid_usage_bitmask; + +/* A bitfield to keep information on in/out traffic is needed to uniquely identify + an endpoint on a device, since the most significant bit which indicates traffic + direction is lacking in the ep_id field (ETRAX epids can handle both in and + out traffic on endpoints that are otherwise identical). The USB framework, however, + relies on them to be handled separately. For example, bulk IN and OUT urbs cannot + be queued in the same list, since they would block each other. */ +static __u32 epid_out_traffic; + +/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line. + Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be cache aligned. */ +static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32))); +static volatile USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4))); +/* Pointers into RxDescList. */ static volatile USB_IN_Desc_t *myNextRxDesc; static volatile USB_IN_Desc_t *myLastRxDesc; static volatile USB_IN_Desc_t *myPrevRxDesc; -static USB_EP_Desc_t TxCtrlEPList[NBR_OF_EP_DESC] __attribute__ ((aligned (4))); -static USB_EP_Desc_t TxBulkEPList[NBR_OF_EP_DESC] __attribute__ ((aligned (4))); +/* EP descriptors must be 32-bit aligned. */ +static volatile USB_EP_Desc_t TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); +static volatile USB_EP_Desc_t TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); +/* After each enabled bulk EP (IN or OUT) we put two disabled EP descriptors with the eol flag set, + causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which + gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the + EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors + in each frame. */ +static volatile USB_EP_Desc_t TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4))); + +static volatile USB_EP_Desc_t TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); +static volatile USB_SB_Desc_t TxIsocSB_zout __attribute__ ((aligned (4))); + +static volatile USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4))); +static volatile USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4))); + +/* A zout transfer makes a memory access at the address of its buf pointer, which means that setting + this buf pointer to 0 will cause an access to the flash. In addition to this, setting sw_len to 0 + results in a 16/32 bytes (depending on DMA burst size) transfer. Instead, we set it to 1, and point + it to this buffer. */ +static int zout_buffer[4] __attribute__ ((aligned (4))); -static USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4))); -static USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4))); - -static struct urb *URB_List[NBR_OF_EP_DESC]; +/* Cache for allocating new EP and SB descriptors. */ static kmem_cache_t *usb_desc_cache; + +/* Cache for the registers allocated in the top half. */ +static kmem_cache_t *top_half_reg_cache; + static struct usb_bus *etrax_usb_bus; -#ifdef USB_DEBUG_DESC -static void dump_urb (struct urb *urb); -#endif +/* This is a circular (double-linked) list of the active urbs for each epid. + The head is never removed, and new urbs are linked onto the list as + urb_entry_t elements. Don't reference urb_list directly; use the wrapper + functions instead. Note that working with these lists might require spinlock + protection. */ +static struct list_head urb_list[NBR_OF_EPIDS]; + +/* Read about the need and usage of this lock in submit_ctrl_urb. */ +static spinlock_t urb_list_lock; + +/* Used when unlinking asynchronously. */ +static struct list_head urb_unlink_list; + +/* Wrappers around the list functions (include/linux/list.h). */ + +static inline int urb_list_empty(int epid) +{ + return list_empty(&urb_list[epid]); +} + +/* Returns first urb for this epid, or NULL if list is empty. */ +static inline urb_t *urb_list_first(int epid) +{ + urb_t *first_urb = 0; + + if (!urb_list_empty(epid)) { + /* Get the first urb (i.e. head->next). */ + urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list); + first_urb = urb_entry->urb; + } + return first_urb; +} + +/* Adds an urb_entry last in the list for this epid. */ +static inline void urb_list_add(urb_t *urb, int epid) +{ + urb_entry_t *urb_entry = (urb_entry_t *)kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG); + assert(urb_entry); + + urb_entry->urb = urb; + list_add_tail(&urb_entry->list, &urb_list[epid]); +} + +/* Search through the list for an element that contains this urb. (The list + is expected to be short and the one we are about to delete will often be + the first in the list.) */ +static inline urb_entry_t *__urb_list_entry(urb_t *urb, int epid) +{ + struct list_head *entry; + struct list_head *tmp; + urb_entry_t *urb_entry; + + list_for_each_safe(entry, tmp, &urb_list[epid]) { + urb_entry = list_entry(entry, urb_entry_t, list); + assert(urb_entry); + assert(urb_entry->urb); + + if (urb_entry->urb == urb) { + return urb_entry; + } + } + return 0; +} + +/* Delete an urb from the list. */ +static inline void urb_list_del(urb_t *urb, int epid) +{ + urb_entry_t *urb_entry = __urb_list_entry(urb, epid); + assert(urb_entry); + + /* Delete entry and free. */ + list_del(&urb_entry->list); + kfree(urb_entry); +} + +/* Move an urb to the end of the list. */ +static inline void urb_list_move_last(urb_t *urb, int epid) +{ + urb_entry_t *urb_entry = __urb_list_entry(urb, epid); + assert(urb_entry); + + list_del(&urb_entry->list); + list_add_tail(&urb_entry->list, &urb_list[epid]); +} + +/* For debug purposes only. */ +static inline void urb_list_dump(int epid) +{ + struct list_head *entry; + struct list_head *tmp; + urb_entry_t *urb_entry; + int i = 0; + + info("Dumping urb list for epid %d", epid); + + list_for_each_safe(entry, tmp, &urb_list[epid]) { + urb_entry = list_entry(entry, urb_entry_t, list); + info(" entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb); + } +} + static void init_rx_buffers(void); -static int etrax_rh_unlink_urb (struct urb *urb); -static void etrax_rh_send_irq(struct urb *urb); -static void etrax_rh_init_int_timer(struct urb *urb); +static int etrax_rh_unlink_urb(urb_t *urb); +static void etrax_rh_send_irq(urb_t *urb); +static void etrax_rh_init_int_timer(urb_t *urb); static void etrax_rh_int_timer_do(unsigned long ptr); -static void etrax_usb_setup_epid(int epid, char devnum, char endpoint, - char packsize, char slow, char out_traffic); -static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, - char slow, int maxp, char out_traffic); +static int etrax_usb_setup_epid(urb_t *urb); +static int etrax_usb_lookup_epid(urb_t *urb); static int etrax_usb_allocate_epid(void); static void etrax_usb_free_epid(int epid); -static void cleanup_sb(USB_SB_Desc_t *sb); -static void etrax_usb_do_ctrl_hw_add(struct urb *urb, int epid, char maxlen); -static void etrax_usb_do_bulk_hw_add(struct urb *urb, int epid, char maxlen); +static int etrax_remove_from_sb_list(urb_t *urb); -static int etrax_usb_submit_ctrl_urb(struct urb *urb); +static void etrax_usb_add_to_bulk_sb_list(urb_t *urb, int epid); +static void etrax_usb_add_to_ctrl_sb_list(urb_t *urb, int epid); +static void etrax_usb_add_to_intr_sb_list(urb_t *urb, int epid); +static void etrax_usb_add_to_isoc_sb_list(urb_t *urb, int epid); + +static int etrax_usb_submit_bulk_urb(urb_t *urb); +static int etrax_usb_submit_ctrl_urb(urb_t *urb); +static int etrax_usb_submit_intr_urb(urb_t *urb); +static int etrax_usb_submit_isoc_urb(urb_t *urb); -static int etrax_usb_submit_urb(struct urb *urb); -static int etrax_usb_unlink_urb(struct urb *urb); +static int etrax_usb_submit_urb(urb_t *urb); +static int etrax_usb_unlink_urb(urb_t *urb); static int etrax_usb_get_frame_number(struct usb_device *usb_dev); static int etrax_usb_allocate_dev(struct usb_device *usb_dev); static int etrax_usb_deallocate_dev(struct usb_device *usb_dev); static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs); static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs); -static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs); +static void etrax_usb_hc_interrupt_top_half(int irq, void *vhc, struct pt_regs *regs); +static void etrax_usb_hc_interrupt_bottom_half(void *data); -static int etrax_rh_submit_urb (struct urb *urb); +/* The following is a list of interrupt handlers for the host controller interrupts we use. + They are called from etrax_usb_hc_interrupt_bottom_half. */ +static void etrax_usb_hc_isoc_eof_interrupt(void); +static void etrax_usb_hc_bulk_eot_interrupt(int timer_induced); +static void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg); +static void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg); +static void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg); + +static int etrax_rh_submit_urb (urb_t *urb); + +/* Forward declaration needed because they are used in the rx interrupt routine. */ +static void etrax_usb_complete_urb(urb_t *urb, int status); +static void etrax_usb_complete_bulk_urb(urb_t *urb, int status); +static void etrax_usb_complete_ctrl_urb(urb_t *urb, int status); +static void etrax_usb_complete_intr_urb(urb_t *urb, int status); +static void etrax_usb_complete_isoc_urb(urb_t *urb, int status); static int etrax_usb_hc_init(void); static void etrax_usb_hc_cleanup(void); @@ -243,65 +451,193 @@ etrax_usb_unlink_urb }; -#ifdef USB_DEBUG_DESC -static void dump_urb(struct urb *urb) -{ - printk("\nurb :0x%08X\n", urb); - printk("next :0x%08X\n", urb->next); - printk("dev :0x%08X\n", urb->dev); - printk("pipe :0x%08X\n", urb->pipe); - printk("status :%d\n", urb->status); - printk("transfer_flags :0x%08X\n", urb->transfer_flags); - printk("transfer_buffer :0x%08X\n", urb->transfer_buffer); - printk("transfer_buffer_length:%d\n", urb->transfer_buffer_length); - printk("actual_length :%d\n", urb->actual_length); - printk("setup_packet :0x%08X\n", urb->setup_packet); - printk("start_frame :%d\n", urb->start_frame); - printk("number_of_packets :%d\n", urb->number_of_packets); - printk("interval :%d\n", urb->interval); - printk("error_count :%d\n", urb->error_count); - printk("context :0x%08X\n", urb->context); - printk("complete :0x%08X\n\n", urb->complete); -} - -static void dump_in_desc(USB_IN_Desc_t *in) -{ - printk("\nUSB_IN_Desc at 0x%08X\n", in); - printk(" sw_len : 0x%04X (%d)\n", in->sw_len, in->sw_len); - printk(" command : 0x%04X\n", in->command); - printk(" next : 0x%08X\n", in->next); - printk(" buf : 0x%08X\n", in->buf); - printk(" hw_len : 0x%04X (%d)\n", in->hw_len, in->hw_len); - printk(" status : 0x%04X\n\n", in->status); -} - -static void dump_sb_desc(USB_SB_Desc_t *sb) -{ - printk("\nUSB_SB_Desc at 0x%08X\n", sb); - printk(" sw_len : 0x%04X (%d)\n", sb->sw_len, sb->sw_len); - printk(" command : 0x%04X\n", sb->command); - printk(" next : 0x%08X\n", sb->next); - printk(" buf : 0x%08X\n\n", sb->buf); +/* Note that these functions are always available in their "__" variants, for use in + error situations. The "__" missing variants are controlled by the USB_DEBUG_DESC/ + USB_DEBUG_URB macros. */ +static void __dump_urb(purb_t purb) +{ + printk("\nurb :0x%08lx\n", (unsigned long)purb); + printk("next :0x%08lx\n", (unsigned long)purb->next); + printk("dev :0x%08lx\n", (unsigned long)purb->dev); + printk("pipe :0x%08x\n", purb->pipe); + printk("status :%d\n", purb->status); + printk("transfer_flags :0x%08x\n", purb->transfer_flags); + printk("transfer_buffer :0x%08lx\n", (unsigned long)purb->transfer_buffer); + printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length); + printk("actual_length :%d\n", purb->actual_length); + printk("setup_packet :0x%08lx\n", (unsigned long)purb->setup_packet); + printk("start_frame :%d\n", purb->start_frame); + printk("number_of_packets :%d\n", purb->number_of_packets); + printk("interval :%d\n", purb->interval); + printk("error_count :%d\n", purb->error_count); + printk("context :0x%08lx\n", (unsigned long)purb->context); + printk("complete :0x%08lx\n\n", (unsigned long)purb->complete); +} + +static void __dump_in_desc(volatile USB_IN_Desc_t *in) +{ + printk("\nUSB_IN_Desc at 0x%08lx\n", (unsigned long)in); + printk(" sw_len : 0x%04x (%d)\n", in->sw_len, in->sw_len); + printk(" command : 0x%04x\n", in->command); + printk(" next : 0x%08lx\n", in->next); + printk(" buf : 0x%08lx\n", in->buf); + printk(" hw_len : 0x%04x (%d)\n", in->hw_len, in->hw_len); + printk(" status : 0x%04x\n\n", in->status); +} + +static void __dump_sb_desc(volatile USB_SB_Desc_t *sb) +{ + char tt = (sb->command & 0x30) >> 4; + char *tt_string; + + switch (tt) { + case 0: + tt_string = "zout"; + break; + case 1: + tt_string = "in"; + break; + case 2: + tt_string = "out"; + break; + case 3: + tt_string = "setup"; + break; + default: + tt_string = "unknown (weird)"; + } + + printk("\n USB_SB_Desc at 0x%08lx\n", (unsigned long)sb); + printk(" command : 0x%04x\n", sb->command); + printk(" rem : %d\n", (sb->command & 0x3f00) >> 8); + printk(" full : %d\n", (sb->command & 0x40) >> 6); + printk(" tt : %d (%s)\n", tt, tt_string); + printk(" intr : %d\n", (sb->command & 0x8) >> 3); + printk(" eot : %d\n", (sb->command & 0x2) >> 1); + printk(" eol : %d\n", sb->command & 0x1); + printk(" sw_len : 0x%04x (%d)\n", sb->sw_len, sb->sw_len); + printk(" next : 0x%08lx\n", sb->next); + printk(" buf : 0x%08lx\n\n", sb->buf); } -static void dump_ep_desc(USB_EP_Desc_t *ep) -{ - printk("\nUSB_EP_Desc at 0x%08X\n", ep); - printk(" hw_len : 0x%04X (%d)\n", ep->hw_len, ep->hw_len); - printk(" command : 0x%08X\n", ep->command); - printk(" sub : 0x%08X\n", ep->sub); - printk(" nep : 0x%08X\n\n", ep->nep); +static void __dump_ep_desc(volatile USB_EP_Desc_t *ep) +{ + printk("\nUSB_EP_Desc at 0x%08lx\n", (unsigned long)ep); + printk(" command : 0x%04x\n", ep->command); + printk(" ep_id : %d\n", (ep->command & 0x1f00) >> 8); + printk(" enable : %d\n", (ep->command & 0x10) >> 4); + printk(" intr : %d\n", (ep->command & 0x8) >> 3); + printk(" eof : %d\n", (ep->command & 0x2) >> 1); + printk(" eol : %d\n", ep->command & 0x1); + printk(" hw_len : 0x%04x (%d)\n", ep->hw_len, ep->hw_len); + printk(" next : 0x%08lx\n", ep->next); + printk(" sub : 0x%08lx\n\n", ep->sub); +} + +static inline void __dump_ep_list(int pipe_type) +{ + volatile USB_EP_Desc_t *ep; + volatile USB_EP_Desc_t *first_ep; + volatile USB_SB_Desc_t *sb; + + switch (pipe_type) + { + case PIPE_BULK: + first_ep = &TxBulkEPList[0]; + break; + case PIPE_CONTROL: + first_ep = &TxCtrlEPList[0]; + break; + case PIPE_INTERRUPT: + first_ep = &TxIntrEPList[0]; + break; + case PIPE_ISOCHRONOUS: + first_ep = &TxIsocEPList[0]; + break; + default: + warn("Cannot dump unknown traffic type"); + return; + } + ep = first_ep; + + printk("\n\nDumping EP list...\n\n"); + + do { + __dump_ep_desc(ep); + /* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */ + sb = ep->sub ? phys_to_virt(ep->sub) : 0; + while (sb) { + __dump_sb_desc(sb); + sb = sb->next ? phys_to_virt(sb->next) : 0; + } + ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next)); + + } while (ep != first_ep); +} + +static inline void __dump_ept_data(int epid) +{ + unsigned long flags; + __u32 r_usb_ept_data; + + if (epid < 0 || epid > 31) { + printk("Cannot dump ept data for invalid epid %d\n", epid); + return; + } + + save_flags(flags); + cli(); + *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); + nop(); + r_usb_ept_data = *R_USB_EPT_DATA; + restore_flags(flags); + + printk("\nR_USB_EPT_DATA = 0x%x for epid %d :\n", r_usb_ept_data, epid); + if (r_usb_ept_data == 0) { + /* No need for more detailed printing. */ + return; + } + printk(" valid : %d\n", (r_usb_ept_data & 0x80000000) >> 31); + printk(" hold : %d\n", (r_usb_ept_data & 0x40000000) >> 30); + printk(" error_count_in : %d\n", (r_usb_ept_data & 0x30000000) >> 28); + printk(" t_in : %d\n", (r_usb_ept_data & 0x08000000) >> 27); + printk(" low_speed : %d\n", (r_usb_ept_data & 0x04000000) >> 26); + printk(" port : %d\n", (r_usb_ept_data & 0x03000000) >> 24); + printk(" error_code : %d\n", (r_usb_ept_data & 0x00c00000) >> 22); + printk(" t_out : %d\n", (r_usb_ept_data & 0x00200000) >> 21); + printk(" error_count_out : %d\n", (r_usb_ept_data & 0x00180000) >> 19); + printk(" max_len : %d\n", (r_usb_ept_data & 0x0003f800) >> 11); + printk(" ep : %d\n", (r_usb_ept_data & 0x00000780) >> 7); + printk(" dev : %d\n", (r_usb_ept_data & 0x0000003f)); } +static inline void __dump_ept_data_list(void) +{ + int i; + printk("Dumping the whole R_USB_EPT_DATA list\n"); + + for (i = 0; i < 32; i++) { + __dump_ept_data(i); + } +} +#ifdef USB_DEBUG_DESC +#define dump_in_desc(...) __dump_in_desc(...) +#define dump_sb_desc(...) __dump_sb_desc(...) +#define dump_ep_desc(...) __dump_ep_desc(...) #else -#define dump_urb(...) do {} while (0) #define dump_in_desc(...) do {} while (0) #define dump_sb_desc(...) do {} while (0) #define dump_ep_desc(...) do {} while (0) #endif +#ifdef USB_DEBUG_URB +#define dump_urb(x) __dump_urb(x) +#else +#define dump_urb(x) do {} while (0) +#endif + static void init_rx_buffers(void) { int i; @@ -315,6 +651,11 @@ RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE)); RxDescList[i].hw_len = 0; RxDescList[i].status = 0; + + /* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc + for the relevant fields.) */ + prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]); + } RxDescList[i].sw_len = RX_DESC_BUF_SIZE; @@ -334,367 +675,254 @@ DBFEXIT; } -static void init_tx_ctrl_ep(void) +static void init_tx_bulk_ep(void) { int i; DBFENTER; - for (i = 0; i < (NBR_OF_EP_DESC - 1); i++) { - TxCtrlEPList[i].hw_len = 0; - TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i); - TxCtrlEPList[i].sub = 0; - TxCtrlEPList[i].nep = virt_to_phys(&TxCtrlEPList[i + 1]); + for (i = 0; i < (NBR_OF_EPIDS - 1); i++) { + CHECK_ALIGN(&TxBulkEPList[i]); + TxBulkEPList[i].hw_len = 0; + TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i); + TxBulkEPList[i].sub = 0; + TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[i + 1]); + + /* Initiate two EPs, disabled and with the eol flag set. No need for any + preserved epid. */ + + /* The first one has the intr flag set so we get an interrupt when the DMA + channel is about to become disabled. */ + CHECK_ALIGN(&TxBulkDummyEPList[i][0]); + TxBulkDummyEPList[i][0].hw_len = 0; + TxBulkDummyEPList[i][0].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) | + IO_STATE(USB_EP_command, eol, yes) | + IO_STATE(USB_EP_command, intr, yes)); + TxBulkDummyEPList[i][0].sub = 0; + TxBulkDummyEPList[i][0].next = virt_to_phys(&TxBulkDummyEPList[i][1]); + + /* The second one. */ + CHECK_ALIGN(&TxBulkDummyEPList[i][1]); + TxBulkDummyEPList[i][1].hw_len = 0; + TxBulkDummyEPList[i][1].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) | + IO_STATE(USB_EP_command, eol, yes)); + TxBulkDummyEPList[i][1].sub = 0; + /* The last dummy's next pointer is the same as the current EP's next pointer. */ + TxBulkDummyEPList[i][1].next = virt_to_phys(&TxBulkEPList[i + 1]); } - - TxCtrlEPList[i].hw_len = 0; - TxCtrlEPList[i].command = IO_STATE(USB_EP_command, eol, yes) | - IO_FIELD(USB_EP_command, epid, i); - TxCtrlEPList[i].sub = 0; - TxCtrlEPList[i].nep = virt_to_phys(&TxCtrlEPList[0]); - - *R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]); - *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start); - + /* Configure the last one. */ + CHECK_ALIGN(&TxBulkEPList[i]); + TxBulkEPList[i].hw_len = 0; + TxBulkEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) | + IO_FIELD(USB_EP_command, epid, i)); + TxBulkEPList[i].sub = 0; + TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[0]); + + /* No need configuring dummy EPs for the last one as it will never be used for + bulk traffic (i == INVALD_EPID at this point). */ + + /* Set up to start on the last EP so we will enable it when inserting traffic + for the first time (imitating the situation where the DMA has stopped + because there was no more traffic). */ + *R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[i]); + /* No point in starting the bulk channel yet. + *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */ DBFEXIT; } -static void init_tx_bulk_ep(void) +static void init_tx_ctrl_ep(void) { int i; DBFENTER; - for (i = 0; i < (NBR_OF_EP_DESC - 1); i++) { - TxBulkEPList[i].hw_len = 0; - TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i); - TxBulkEPList[i].sub = 0; - TxBulkEPList[i].nep = virt_to_phys(&TxBulkEPList[i + 1]); + for (i = 0; i < (NBR_OF_EPIDS - 1); i++) { + CHECK_ALIGN(&TxCtrlEPList[i]); + TxCtrlEPList[i].hw_len = 0; + TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i); + TxCtrlEPList[i].sub = 0; + TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[i + 1]); } - TxBulkEPList[i].hw_len = 0; - TxBulkEPList[i].command = IO_STATE(USB_EP_command, eol, yes) | - IO_FIELD(USB_EP_command, epid, i); + CHECK_ALIGN(&TxCtrlEPList[i]); + TxCtrlEPList[i].hw_len = 0; + TxCtrlEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) | + IO_FIELD(USB_EP_command, epid, i)); - TxBulkEPList[i].sub = 0; - TxBulkEPList[i].nep = virt_to_phys(&TxBulkEPList[0]); + TxCtrlEPList[i].sub = 0; + TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[0]); - *R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[0]); - *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); + *R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]); + *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start); DBFEXIT; } + static void init_tx_intr_ep(void) { int i; DBFENTER; - TxIntrSB_zout.sw_len = 0; + /* Read comment at zout_buffer declaration for an explanation to this. */ + TxIntrSB_zout.sw_len = 1; TxIntrSB_zout.next = 0; - TxIntrSB_zout.buf = 0; - TxIntrSB_zout.command = IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, zout) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes); + TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]); + TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) | + IO_STATE(USB_SB_command, tt, zout) | + IO_STATE(USB_SB_command, full, yes) | + IO_STATE(USB_SB_command, eot, yes) | + IO_STATE(USB_SB_command, eol, yes)); for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) { + CHECK_ALIGN(&TxIntrEPList[i]); TxIntrEPList[i].hw_len = 0; - TxIntrEPList[i].command = IO_STATE(USB_EP_command, eof, yes) | - IO_STATE(USB_EP_command, enable, yes) | - IO_FIELD(USB_EP_command, epid, 0); + TxIntrEPList[i].command = + (IO_STATE(USB_EP_command, eof, yes) | + IO_STATE(USB_EP_command, enable, yes) | + IO_FIELD(USB_EP_command, epid, INVALID_EPID)); TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout); - TxIntrEPList[i].nep = virt_to_phys(&TxIntrEPList[i + 1]); + TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]); } + CHECK_ALIGN(&TxIntrEPList[i]); TxIntrEPList[i].hw_len = 0; TxIntrEPList[i].command = - IO_STATE(USB_EP_command, eof, yes) | - IO_STATE(USB_EP_command, enable, yes) | - IO_FIELD(USB_EP_command, epid, 0); + (IO_STATE(USB_EP_command, eof, yes) | + IO_STATE(USB_EP_command, eol, yes) | + IO_STATE(USB_EP_command, enable, yes) | + IO_FIELD(USB_EP_command, epid, INVALID_EPID)); TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout); - TxIntrEPList[i].nep = virt_to_phys(&TxIntrEPList[0]); + TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[0]); *R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]); *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start); - DBFEXIT; } - -static int etrax_usb_unlink_intr_urb(struct urb *urb) +static void init_tx_isoc_ep(void) { - USB_EP_Desc_t *tmp_ep; - USB_EP_Desc_t *first_ep; - - USB_EP_Desc_t *ep_desc; - - int epid; - char devnum; - char endpoint; - char slow; - int maxlen; - char out_traffic; int i; DBFENTER; - - devnum = usb_pipedevice(urb->pipe); - endpoint = usb_pipeendpoint(urb->pipe); - slow = usb_pipeslow(urb->pipe); - maxlen = usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe)); - out_traffic = usb_pipeout(urb->pipe); - - epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic); - if (epid == -1) { - err("Trying to unlink urb that is not in traffic queue!!"); - return -1; /* fix this */ - } - - *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, stop); - /* Somehow wait for the DMA to finish current activities */ - i = jiffies + 100; - while (jiffies < i); - first_ep = &TxIntrEPList[0]; - tmp_ep = first_ep; + /* Read comment at zout_buffer declaration for an explanation to this. */ + TxIsocSB_zout.sw_len = 1; + TxIsocSB_zout.next = 0; + TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]); + TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) | + IO_STATE(USB_SB_command, tt, zout) | + IO_STATE(USB_SB_command, full, yes) | + IO_STATE(USB_SB_command, eot, yes) | + IO_STATE(USB_SB_command, eol, yes)); - do { - if (IO_EXTRACT(USB_EP_command, epid, ((USB_EP_Desc_t *)phys_to_virt(tmp_ep->nep))->command) - == epid) { - /* Unlink it !!! */ - dbg_intr("Found urb to unlink for epid %d", epid); - - ep_desc = phys_to_virt(tmp_ep->nep); - tmp_ep->nep = ep_desc->nep; - kmem_cache_free(usb_desc_cache, phys_to_virt(ep_desc->sub)); - kmem_cache_free(usb_desc_cache, ep_desc); - } + /* The last isochronous EP descriptor is a dummy. */ + + for (i = 0; i < (NBR_OF_EPIDS - 1); i++) { + CHECK_ALIGN(&TxIsocEPList[i]); + TxIsocEPList[i].hw_len = 0; + TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i); + TxIsocEPList[i].sub = 0; + TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]); + } - tmp_ep = phys_to_virt(tmp_ep->nep); - - } while (tmp_ep != first_ep); + CHECK_ALIGN(&TxIsocEPList[i]); + TxIsocEPList[i].hw_len = 0; - /* We should really try to move the EP register to an EP that is not removed - instead of restarting, but this will work too */ - *R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]); - *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start); + /* Must enable the last EP descr to get eof interrupt. */ + TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) | + IO_STATE(USB_EP_command, eof, yes) | + IO_STATE(USB_EP_command, eol, yes) | + IO_FIELD(USB_EP_command, epid, INVALID_EPID)); + TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout); + TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]); - clear_bit(epid, (void *)&ep_really_active); - URB_List[epid] = NULL; - etrax_usb_free_epid(epid); + *R_DMA_CH8_SUB3_EP = virt_to_phys(&TxIsocEPList[0]); + *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start); DBFEXIT; - - return 0; } -void etrax_usb_do_intr_recover(int epid) +static void etrax_usb_unlink_intr_urb(urb_t *urb) { - USB_EP_Desc_t *first_ep, *tmp_ep; - - first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP); - tmp_ep = first_ep; - - do { - if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid && - !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) { - tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes); - } - - tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->nep); - - } while (tmp_ep != first_ep); -} + volatile USB_EP_Desc_t *first_ep; /* First EP in the list. */ + volatile USB_EP_Desc_t *curr_ep; /* Current EP, the iterator. */ + volatile USB_EP_Desc_t *next_ep; /* The EP after current. */ + volatile USB_EP_Desc_t *unlink_ep; /* The one we should remove from the list. */ -static int etrax_usb_submit_intr_urb(struct urb *urb) -{ - USB_EP_Desc_t *tmp_ep; - USB_EP_Desc_t *first_ep; - int epid; - char devnum; - char endpoint; - char maxlen; - char out_traffic; - char slow; - int interval; - int i; - - etrax_urb_priv_t *urb_priv; - - DBFENTER; - - devnum = usb_pipedevice(urb->pipe); - endpoint = usb_pipeendpoint(urb->pipe); - maxlen = usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe)); - out_traffic = usb_pipeout(urb->pipe); - slow = usb_pipeslow(urb->pipe); - interval = urb->interval; + /* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */ - dbg_intr("Intr traffic for dev %d, endpoint %d, maxlen %d, slow %d", - devnum, endpoint, maxlen, slow); - - epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic); - if (epid == -1) { - epid = etrax_usb_allocate_epid(); - if (epid == -1) { - /* We're out of endpoints, return some error */ - err("We're out of endpoints"); - return -ENOMEM; - } - /* Now we have to fill in this ep */ - etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic); - } - /* Ok, now we got valid endpoint, lets insert some traffic */ + DBFENTER; - urb_priv = (etrax_urb_priv_t *)kmalloc(sizeof(etrax_urb_priv_t), GFP_KERNEL); - urb_priv->first_sb = 0; - urb_priv->rx_offset = 0; - urb_priv->eot = 0; - INIT_LIST_HEAD(&urb_priv->ep_in_list); - urb->hcpriv = urb_priv; + epid = ((etrax_urb_priv_t *)urb->hcpriv)->epid; - /* This is safe since there cannot be any other URB's for this epid */ - URB_List[epid] = urb; -#if 0 - first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP); -#else first_ep = &TxIntrEPList[0]; -#endif + curr_ep = first_ep; + - /* Round of the interval to 2^n, it is obvious that this code favours - smaller numbers, but that is actually a good thing */ - for (i = 0; interval; i++) { - interval = interval >> 1; - } + /* Note that this loop removes all EP descriptors with this epid. This assumes + that all EP descriptors belong to the one and only urb for this epid. */ - urb->interval = interval = 1 << (i - 1); + do { + next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next); - dbg_intr("Interval rounded to %d", interval); + if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) { - tmp_ep = first_ep; - i = 0; - do { - if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) { - if ((i % interval) == 0) { - /* Insert the traffic ep after tmp_ep */ - USB_EP_Desc_t *traffic_ep; - USB_SB_Desc_t *traffic_sb; + dbg_intr("Found EP to unlink for epid %d", epid); + + /* This is the one we should unlink. */ + unlink_ep = next_ep; - traffic_ep = (USB_EP_Desc_t *) - kmem_cache_alloc(usb_desc_cache, GFP_KERNEL); - traffic_sb = (USB_SB_Desc_t *) - kmem_cache_alloc(usb_desc_cache, GFP_KERNEL); - - traffic_ep->hw_len = 0; - traffic_ep->command = IO_FIELD(USB_EP_command, epid, epid) | - IO_STATE(USB_EP_command, enable, yes); - traffic_ep->sub = virt_to_phys(traffic_sb); - - if (usb_pipein(urb->pipe)) { - traffic_sb->sw_len = urb->transfer_buffer_length ? - (urb->transfer_buffer_length - 1) / maxlen + 1 : 0; - traffic_sb->next = 0; - traffic_sb->buf = 0; - traffic_sb->command = IO_FIELD(USB_SB_command, rem, - urb->transfer_buffer_length % maxlen) | - IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes); - - } else if (usb_pipeout(urb->pipe)) { - traffic_sb->sw_len = urb->transfer_buffer_length; - traffic_sb->next = 0; - traffic_sb->buf = virt_to_phys(urb->transfer_buffer); - traffic_sb->command = IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, out) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes) | - IO_STATE(USB_SB_command, full, yes); - } + /* Actually unlink the EP from the DMA list. */ + curr_ep->next = unlink_ep->next; - traffic_ep->nep = tmp_ep->nep; - tmp_ep->nep = virt_to_phys(traffic_ep); - dbg_intr("One ep sucessfully inserted"); - } - i++; + /* Wait until the DMA is no longer at this descriptor. */ + while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep)); + + /* Now we are free to remove it and its SB descriptor. + Note that it is assumed here that there is only one sb in the + sb list for this ep. */ + kmem_cache_free(usb_desc_cache, phys_to_virt(unlink_ep->sub)); + kmem_cache_free(usb_desc_cache, (USB_EP_Desc_t *)unlink_ep); } - tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->nep); - } while (tmp_ep != first_ep); - set_bit(epid, (void *)&ep_really_active); - - *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start); + curr_ep = phys_to_virt(curr_ep->next); + + } while (curr_ep != first_ep); - DBFEXIT; - - return 0; } - -static void handle_intr_transfer_attn(int epid, int status) +void etrax_usb_do_intr_recover(int epid) { - struct urb *old_urb; + USB_EP_Desc_t *first_ep, *tmp_ep; DBFENTER; + + first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP); + tmp_ep = first_ep; - old_urb = URB_List[epid]; - - /* if (status == 0 && IN) find data and copy to urb */ - if (status == 0 && usb_pipein(old_urb->pipe)) { - unsigned long flags; - etrax_urb_priv_t *urb_priv; - struct list_head *entry; - struct in_chunk *in; - - urb_priv = (etrax_urb_priv_t *)old_urb->hcpriv; + /* What this does is simply to walk the list of interrupt + ep descriptors and enable those that are disabled. */ + + do { + if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid && + !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) { + tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes); + } - save_flags(flags); - cli(); + tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next); + + } while (tmp_ep != first_ep); - list_for_each(entry, &urb_priv->ep_in_list) { - in = list_entry(entry, struct in_chunk, list); - memcpy(old_urb->transfer_buffer, in->data, in->length); - old_urb->actual_length = in->length; - old_urb->status = status; - - if (old_urb->complete) { - old_urb->complete(old_urb); - } - - list_del(entry); - kfree(in->data); - kfree(in); - } - - restore_flags(flags); - - } else if (status != 0) { - warn("Some sort of error for INTR EP !!!!"); -#ifdef ETRAX_USB_INTR_ERROR_FATAL - /* This means that an INTR error is fatal for that endpoint */ - etrax_usb_unlink_intr_urb(old_urb); - old_urb->status = status; - if (old_urb->complete) { - old_urb->complete(old_urb); - } -#else - /* In this case we reenable the disabled endpoint(s) */ - etrax_usb_do_intr_recover(epid); -#endif - } - - DBFEXIT; + + DBFEXIT; } -static int etrax_rh_unlink_urb (struct urb *urb) +static int etrax_rh_unlink_urb (urb_t *urb) { etrax_hc_t *hc; @@ -711,16 +939,12 @@ return 0; } -static void etrax_rh_send_irq(struct urb *urb) +static void etrax_rh_send_irq(urb_t *urb) { __u16 data = 0; etrax_hc_t *hc = urb->dev->bus->hcpriv; -// static prev_wPortStatus_1 = 0; -// static prev_wPortStatus_2 = 0; - /* DBFENTER; */ - - + /* dbg_rh("R_USB_FM_NUMBER : 0x%08X", *R_USB_FM_NUMBER); dbg_rh("R_USB_FM_REMAINING: 0x%08X", *R_USB_FM_REMAINING); @@ -730,6 +954,8 @@ data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0; *((__u16 *)urb->transfer_buffer) = cpu_to_le16(data); + /* FIXME: Why is actual_length set to 1 when data is 2 bytes? + Since only 1 byte is used, why not declare data as __u8? */ urb->actual_length = 1; urb->status = 0; @@ -744,7 +970,7 @@ /* DBFEXIT; */ } -static void etrax_rh_init_int_timer(struct urb *urb) +static void etrax_rh_init_int_timer(urb_t *urb) { etrax_hc_t *hc; @@ -755,6 +981,8 @@ init_timer(&hc->rh.rh_int_timer); hc->rh.rh_int_timer.function = etrax_rh_int_timer_do; hc->rh.rh_int_timer.data = (unsigned long)urb; + /* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped + to 0, and the rest to the nearest lower 10 ms. */ hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000); add_timer(&hc->rh.rh_int_timer); @@ -763,12 +991,12 @@ static void etrax_rh_int_timer_do(unsigned long ptr) { - struct urb *urb; + urb_t *urb; etrax_hc_t *hc; /* DBFENTER; */ - urb = (struct urb*)ptr; + urb = (urb_t*)ptr; hc = urb->dev->bus->hcpriv; if (hc->rh.send) { @@ -780,44 +1008,81 @@ /* DBFEXIT; */ } -static void etrax_usb_setup_epid(int epid, char devnum, char endpoint, char packsize, char slow, char out_traffic) +static int etrax_usb_setup_epid(urb_t *urb) { + int epid; + char devnum, endpoint, out_traffic, slow; + int maxlen; unsigned long flags; - - DBFENTER; - save_flags(flags); - cli(); + DBFENTER; - if (test_bit(epid, (void *)&ep_usage_bitmask)) { - restore_flags(flags); - - warn("Trying to setup used epid %d", epid); + epid = etrax_usb_lookup_epid(urb); + if ((epid != -1)){ + /* An epid that fits this urb has been found. */ DBFEXIT; - return; + return epid; } - set_bit(epid, (void *)&ep_usage_bitmask); + /* We must find and initiate a new epid for this urb. */ + epid = etrax_usb_allocate_epid(); + + if (epid == -1) { + /* Failed to allocate a new epid. */ + DBFEXIT; + return epid; + } + + /* We now have a new epid to use. Initiate it. */ + set_bit(epid, (void *)&epid_usage_bitmask); + devnum = usb_pipedevice(urb->pipe); + endpoint = usb_pipeendpoint(urb->pipe); + slow = usb_pipeslow(urb->pipe); + maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { + /* We want both IN and OUT control traffic to be put on the same EP/SB list. */ + out_traffic = 1; + } else { + out_traffic = usb_pipeout(urb->pipe); + } + + save_flags(flags); + cli(); + *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop(); - *R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) | - IO_FIELD(R_USB_EPT_DATA, ep, endpoint) | - IO_FIELD(R_USB_EPT_DATA, dev, devnum) | - IO_FIELD(R_USB_EPT_DATA, max_len, packsize) | - IO_FIELD(R_USB_EPT_DATA, low_speed, slow); - - if (out_traffic) - set_bit(epid, (void *)&ep_out_traffic); - else - clear_bit(epid, (void *)&ep_out_traffic); + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + *R_USB_EPT_DATA_ISO = IO_STATE(R_USB_EPT_DATA_ISO, valid, yes) | + /* FIXME: Change any to the actual port? */ + IO_STATE(R_USB_EPT_DATA_ISO, port, any) | + IO_FIELD(R_USB_EPT_DATA_ISO, max_len, maxlen) | + IO_FIELD(R_USB_EPT_DATA_ISO, ep, endpoint) | + IO_FIELD(R_USB_EPT_DATA_ISO, dev, devnum); + } else { + *R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) | + IO_FIELD(R_USB_EPT_DATA, low_speed, slow) | + /* FIXME: Change any to the actual port? */ + IO_STATE(R_USB_EPT_DATA, port, any) | + IO_FIELD(R_USB_EPT_DATA, max_len, maxlen) | + IO_FIELD(R_USB_EPT_DATA, ep, endpoint) | + IO_FIELD(R_USB_EPT_DATA, dev, devnum); + } + restore_flags(flags); - dbg_ep("Setting up ep_id %d with devnum %d, endpoint %d and max_len %d (%s)", - epid, devnum, endpoint, packsize, out_traffic ? "OUT" : "IN"); + if (out_traffic) { + set_bit(epid, (void *)&epid_out_traffic); + } else { + clear_bit(epid, (void *)&epid_out_traffic); + } + + dbg_epid("Setting up epid %d with devnum %d, endpoint %d and max_len %d (%s)", + epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN"); DBFEXIT; + return epid; } static void etrax_usb_free_epid(int epid) @@ -826,7 +1091,7 @@ DBFENTER; - if (!test_bit(epid, (void *)&ep_usage_bitmask)) { + if (!test_bit(epid, (void *)&epid_usage_bitmask)) { warn("Trying to free unused epid %d", epid); DBFEXIT; return; @@ -837,56 +1102,81 @@ *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop(); - while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) - printk("+"); + while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)); + /* This will, among other things, set the valid field to 0. */ *R_USB_EPT_DATA = 0; - clear_bit(epid, (void *)&ep_usage_bitmask); - restore_flags(flags); - dbg_ep("epid: %d freed", epid); + clear_bit(epid, (void *)&epid_usage_bitmask); + + + dbg_epid("Freed epid %d", epid); DBFEXIT; } - -static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp, char out_traffic) +static int etrax_usb_lookup_epid(urb_t *urb) { int i; - unsigned long flags; __u32 data; - + char devnum, endpoint, slow, out_traffic; + int maxlen; + unsigned long flags; + DBFENTER; - save_flags(flags); - cli(); + devnum = usb_pipedevice(urb->pipe); + endpoint = usb_pipeendpoint(urb->pipe); + slow = usb_pipeslow(urb->pipe); + maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { + /* We want both IN and OUT control traffic to be put on the same EP/SB list. */ + out_traffic = 1; + } else { + out_traffic = usb_pipeout(urb->pipe); + } + + /* Step through att epids. */ + for (i = 0; i < NBR_OF_EPIDS; i++) { + if (test_bit(i, (void *)&epid_usage_bitmask) && + test_bit(i, (void *)&epid_out_traffic) == out_traffic) { - /* Skip first ep_id since it is reserved when intr. or iso traffic is used */ - for (i = 0; i < NBR_OF_EP_DESC; i++) { - if (test_bit(i, (void *)&ep_usage_bitmask) && - test_bit(i, (void *)&ep_out_traffic) == out_traffic) { + save_flags(flags); + cli(); *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i); nop(); - data = *R_USB_EPT_DATA; - if ((IO_MASK(R_USB_EPT_DATA, valid) & data) && - (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) && - (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) && - (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) && - (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxp)) { + + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + data = *R_USB_EPT_DATA_ISO; restore_flags(flags); - - dbg_ep("Found ep_id %d for devnum %d, endpoint %d (%s)", - i, devnum, endpoint, out_traffic ? "OUT" : "IN"); - DBFEXIT; - return i; + + if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) && + (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) && + (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) && + (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) { + dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)", + i, devnum, endpoint, out_traffic ? "OUT" : "IN"); + DBFEXIT; + return i; + } + } else { + data = *R_USB_EPT_DATA; + restore_flags(flags); + + if ((IO_MASK(R_USB_EPT_DATA, valid) & data) && + (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) && + (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) && + (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) && + (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) { + dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)", + i, devnum, endpoint, out_traffic ? "OUT" : "IN"); + DBFEXIT; + return i; + } } } } - restore_flags(flags); - - dbg_ep("Found no ep_id for devnum %d, endpoint %d (%s)", - devnum, endpoint, out_traffic ? "OUT" : "IN"); DBFEXIT; return -1; } @@ -897,434 +1187,993 @@ DBFENTER; - for (i = 0; i < NBR_OF_EP_DESC; i++) { - if (!test_bit(i, (void *)&ep_usage_bitmask)) { - dbg_ep("Found free ep_id at %d", i); + for (i = 0; i < NBR_OF_EPIDS; i++) { + if (!test_bit(i, (void *)&epid_usage_bitmask)) { + dbg_epid("Found free epid %d", i); DBFEXIT; return i; } } - dbg_ep("Found no free ep_id's"); + dbg_epid("Found no free epids"); DBFEXIT; return -1; } -static int etrax_usb_submit_bulk_urb(struct urb *urb) +static int etrax_usb_submit_urb(urb_t *urb) { - int epid; - char devnum; - char endpoint; - char maxlen; - char out_traffic; - char slow; - - struct urb *tmp_urb; - - unsigned long flags; + etrax_hc_t *hc; + int ret = -EINVAL; DBFENTER; - devnum = usb_pipedevice(urb->pipe); - endpoint = usb_pipeendpoint(urb->pipe); - maxlen = usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe)); - out_traffic = usb_pipeout(urb->pipe); - slow = usb_pipeslow(urb->pipe); + if (!urb->dev || !urb->dev->bus) { + return -ENODEV; + } + if (urb->next != NULL) { + /* Is it possible for urb to be the head of a list of urbs (via the urb's + next pointer), used for example in drivers for isochronous traffic. + I haven't seen a device driver that relies on it being used for submit + or unlink, so we warn about it and ignore it. */ + warn("Urbs are linked, ignoring."); + } + if (urb->timeout) { + /* FIXME. */ + warn("urb->timeout specified, ignoring."); + } + + hc = (etrax_hc_t*)urb->dev->bus->hcpriv; - epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic); - if (epid == -1) { - epid = etrax_usb_allocate_epid(); - if (epid == -1) { - /* We're out of endpoints, return some error */ - err("We're out of endpoints"); - return -ENOMEM; + if (usb_pipedevice(urb->pipe) == hc->rh.devnum) { + /* This request is for the Virtual Root Hub. */ + ret = etrax_rh_submit_urb(urb); + + } else if (usb_pipetype(urb->pipe) == PIPE_BULK) { + + ret = etrax_usb_submit_bulk_urb(urb); + + } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { + + ret = etrax_usb_submit_ctrl_urb(urb); + + } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { + int bustime; + + if (urb->bandwidth == 0) { + bustime = usb_check_bandwidth(urb->dev, urb); + if (bustime < 0) { + ret = bustime; + } else { + ret = etrax_usb_submit_intr_urb(urb); + if (ret == 0) + usb_claim_bandwidth(urb->dev, urb, bustime, 0); + } + } else { + /* Bandwidth already set. */ + ret = etrax_usb_submit_intr_urb(urb); + } + + } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + int bustime; + + if (urb->bandwidth == 0) { + bustime = usb_check_bandwidth(urb->dev, urb); + if (bustime < 0) { + ret = bustime; + } else { + ret = etrax_usb_submit_isoc_urb(urb); + if (ret == 0) + usb_claim_bandwidth(urb->dev, urb, bustime, 0); + } + } else { + /* Bandwidth already set. */ + ret = etrax_usb_submit_isoc_urb(urb); } - /* Now we have to fill in this ep */ - etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic); } - /* Ok, now we got valid endpoint, lets insert some traffic */ - urb->status = -EINPROGRESS; + DBFEXIT; - save_flags(flags); - cli(); - - if (URB_List[epid]) { - /* Find end of list and add */ - for (tmp_urb = URB_List[epid]; tmp_urb->next; tmp_urb = tmp_urb->next) - dump_urb(tmp_urb); + return ret; +} + +static int etrax_usb_unlink_urb(urb_t *urb) +{ + etrax_hc_t *hc; + etrax_urb_priv_t *urb_priv; + int epid; + + DBFENTER; + + if (!urb) { + return -EINVAL; + } + if (!urb->dev || !urb->dev->bus) { + return -ENODEV; + } + if (!urb->hcpriv) { + /* This happens if a device driver calls unlink on an urb that + was never submitted (lazy driver). */ + return 0; + } + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + /* FIXME. */ + /* If USB_ASYNC_UNLINK is set: + unlink + move to a separate urb list + call complete at next sof with ECONNRESET + + If not: + wait 1 ms + unlink + call complete with ENOENT + */ + warn("USB_ASYNC_UNLINK set, ignoring."); + } + + /* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking, + but that doesn't work for interrupt and isochronous traffic since they are completed + repeatedly, and urb->status is set then. That may in itself be a bug though. */ + + hc = urb->dev->bus->hcpriv; + urb_priv = (etrax_urb_priv_t *)urb->hcpriv; + epid = urb_priv->epid; + + /* Set the urb status (synchronous unlink). */ + urb->status = -ENOENT; + urb_priv->urb_state = UNLINK; + + if (usb_pipedevice(urb->pipe) == hc->rh.devnum) { + int ret; + ret = etrax_rh_unlink_urb(urb); + DBFEXIT; + return ret; - tmp_urb->next = urb; - restore_flags(flags); - } else { - /* If this is the first URB, add the URB and do HW add */ - URB_List[epid] = urb; - restore_flags(flags); - etrax_usb_do_bulk_hw_add(urb, epid, maxlen); - } + } else if (usb_pipetype(urb->pipe) == PIPE_BULK) { + + dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb); + + if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) { + /* The EP was enabled, disable it and wait. */ + TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); + + /* Ah, the luxury of busy-wait. */ + while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid])); + } + /* Kicking dummy list out of the party. */ + TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]); + + } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { + + dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb); + + if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) { + /* The EP was enabled, disable it and wait. */ + TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); + + /* Ah, the luxury of busy-wait. */ + while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid])); + } + + } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { + + dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb); + + /* Separate function because it's a tad more complicated. */ + etrax_usb_unlink_intr_urb(urb); + + } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb); + + if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) { + /* The EP was enabled, disable it and wait. */ + TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); + + /* Ah, the luxury of busy-wait. */ + while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid])); + } + } + + /* Note that we need to remove the urb from the urb list *before* removing its SB + descriptors. (This means that the isoc eof handler might get a null urb when we + are unlinking the last urb.) */ + + urb_list_del(urb, epid); + + if (usb_pipetype(urb->pipe) == PIPE_BULK) { + + TxBulkEPList[epid].sub = 0; + etrax_remove_from_sb_list(urb); + + } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { + + TxCtrlEPList[epid].sub = 0; + etrax_remove_from_sb_list(urb); + + } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { + + /* Sanity check (should never happen). */ + assert(urb_list_empty(epid)); + + /* Release allocated bandwidth. */ + usb_release_bandwidth(urb->dev, urb, 0); + + } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + + if (usb_pipeout(urb->pipe)) { + /* FIXME: If the SB list isn't empty at this point, we need to set up + the EP descriptor again. */ + } else { + /* For in traffic there is only one SB descriptor for each EP even + though there may be several urbs (all urbs point at the same SB). */ + if (urb_list_empty(epid)) { + /* No more urbs, remove the SB. */ + TxIsocEPList[epid].sub = 0; + etrax_remove_from_sb_list(urb); + } + } + /* Release allocated bandwidth. */ + usb_release_bandwidth(urb->dev, urb, 1); + } + + /* Must be done before calling completion handler. */ + kfree(urb_priv); + urb->hcpriv = 0; + + if (urb->complete) { + urb->complete(urb); + } + + /* Free the epid if urb list is empty. */ + if (urb_list_empty(epid)) { + etrax_usb_free_epid(epid); + } + + DBFEXIT; + return 0; +} + +static int etrax_usb_get_frame_number(struct usb_device *usb_dev) +{ + DBFENTER; DBFEXIT; + return (*R_USB_FM_NUMBER & 0x7ff); +} +static int etrax_usb_allocate_dev(struct usb_device *usb_dev) +{ + DBFENTER; + DBFEXIT; return 0; } -static void etrax_usb_do_bulk_hw_add(struct urb *urb, int epid, char maxlen) +static int etrax_usb_deallocate_dev(struct usb_device *usb_dev) { - USB_SB_Desc_t *sb_desc_1; + DBFENTER; + DBFEXIT; + return 0; +} + +static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs) +{ + DBFENTER; + + /* This interrupt handler could be used when unlinking EP descriptors. */ + + if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) { + USB_EP_Desc_t *ep; + + //dbg_bulk("dma8_sub0_descr (BULK) intr."); + + /* It should be safe clearing the interrupt here, since we don't expect to get a new + one until we restart the bulk channel. */ + *R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do); + /* Wait while the DMA is running (though we don't expect it to be). */ + while (*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd)); + + /* Advance the DMA to the next EP descriptor. */ + ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP); + + //dbg_bulk("descr intr: DMA is at 0x%lx", (unsigned long)ep); + + /* ep->next is already a physical address; no need for a virt_to_phys. */ + *R_DMA_CH8_SUB0_EP = ep->next; + + /* Start the DMA bulk channel again. */ + *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); + } + if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) { + dbg_ctrl("dma8_sub1_descr (CTRL) intr."); + *R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do); + } + if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) { + dbg_intr("dma8_sub2_descr (INTR) intr."); + *R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do); + } + if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) { + dbg_isoc("dma8_sub3_descr (ISOC) intr."); + *R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do); + } + + DBFEXIT; +} + +static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs) +{ + urb_t *urb; etrax_urb_priv_t *urb_priv; + int epid = 0; + unsigned long flags; - unsigned long flags; + /* Isoc diagnostics. */ + static int curr_fm = 0; + static int prev_fm = 0; + + DBFENTER; + + /* Clear this interrupt. */ + *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do); + + /* Note that this while loop assumes that all packets span only + one rx descriptor. */ + + /* The reason we cli here is that we call the driver's callback functions. */ + save_flags(flags); + cli(); + + + while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) { + + epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status); + urb = urb_list_first(epid); + + //printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb); + + if (!urb) { + err("No urb for epid %d in rx interrupt", epid); + __dump_ept_data(epid); + goto skip_out; + } + + /* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since + ctrl pipes are not. */ + + if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) { + warn("error in rx desc->status, epid %d, first urb = 0x%lx", + epid, (unsigned long)urb); + __dump_in_desc(myNextRxDesc); + etrax_usb_complete_urb(urb, -EPROTO); + goto skip_out; + } + + urb_priv = (etrax_urb_priv_t *)urb->hcpriv; + assert(urb_priv); + + if ((usb_pipetype(urb->pipe) == PIPE_BULK) || + (usb_pipetype(urb->pipe) == PIPE_CONTROL) || + (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) { + + if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) { + /* We get nodata for empty data transactions, and the rx descriptor's + hw_len field is not valid in that case. No data to copy in other + words. */ + } else { + /* Make sure the data fits in the buffer. */ + assert(urb_priv->rx_offset + myNextRxDesc->hw_len + <= urb->transfer_buffer_length); + + memcpy(urb->transfer_buffer + urb_priv->rx_offset, + phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len); + urb_priv->rx_offset += myNextRxDesc->hw_len; + } + + if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) { + etrax_usb_complete_urb(urb, 0); + } + + } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + + iso_packet_descriptor_t *packet; + + if (urb_priv->urb_state == UNLINK) { + info("Ignoring rx data for urb being unlinked."); + goto skip_out; + } else if (urb_priv->urb_state == NOT_STARTED) { + info("What? Got rx data for urb that isn't started?"); + goto skip_out; + } + + packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter]; + packet->status = 0; + + if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) { + /* We get nodata for empty data transactions, and the rx descriptor's + hw_len field is not valid in that case. We copy 0 bytes however to + stay in synch. */ + packet->actual_length = 0; + } else { + packet->actual_length = myNextRxDesc->hw_len; + /* Make sure the data fits in the buffer. */ + assert(packet->actual_length <= packet->length); + + memcpy(urb->transfer_buffer + packet->offset, + phys_to_virt(myNextRxDesc->buf), packet->actual_length); + } + + /* Increment the packet counter. */ + urb_priv->isoc_packet_counter++; + + /* Note that we don't care about the eot field in the rx descriptor's status. + It will always be set for isoc traffic. */ + if (urb->number_of_packets == urb_priv->isoc_packet_counter) { + + /* Out-of-synch diagnostics. */ + curr_fm = (*R_USB_FM_NUMBER & 0x7ff); + if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) { + warn("Out of synch? Previous frame = %d, current frame = %d", + prev_fm, curr_fm); + } + prev_fm = curr_fm; + + /* Complete the urb with status OK. */ + etrax_usb_complete_isoc_urb(urb, 0); + + /* Must set this to 0 since this urb is still active after + completion. */ + urb_priv->isoc_packet_counter = 0; + } + } + + skip_out: + + /* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr + has the same layout as USB_IN_Desc for the relevant fields.) */ + prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc); + + myPrevRxDesc = myNextRxDesc; + myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol); + myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol); + myLastRxDesc = myPrevRxDesc; + + myNextRxDesc->status = 0; + myNextRxDesc = phys_to_virt(myNextRxDesc->next); + } + + restore_flags(flags); + + DBFEXIT; + +} + + +/* This function will unlink the SB descriptors associated with this urb. */ +static int etrax_remove_from_sb_list(urb_t *urb) +{ + USB_SB_Desc_t *next_sb, *first_sb, *last_sb; + etrax_urb_priv_t *urb_priv; + int i = 0; DBFENTER; - urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_KERNEL); - sb_desc_1 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL); + urb_priv = (etrax_urb_priv_t *)urb->hcpriv; + assert(urb_priv); - if (usb_pipeout(urb->pipe)) { + /* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor + doesn't really need to be disabled, it's just that we expect it to be. */ + if (usb_pipetype(urb->pipe) == PIPE_BULK) { + assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable))); + } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { + assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable))); + } - dbg_bulk("Bulk transfer for epid %d is OUT", epid); - dbg_bulk("transfer_buffer_length == %d", urb->transfer_buffer_length); - dbg_bulk("actual_length == %d", urb->actual_length); + first_sb = urb_priv->first_sb; + last_sb = urb_priv->last_sb; + assert(first_sb); + assert(last_sb); + + while (first_sb != last_sb) { + next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next); + kmem_cache_free(usb_desc_cache, first_sb); + first_sb = next_sb; + i++; + } + kmem_cache_free(usb_desc_cache, last_sb); + i++; + dbg_sb("%d SB descriptors freed", i); + /* Compare i with urb->number_of_packets for Isoc traffic. + Should be same when calling unlink_urb */ + + DBFEXIT; + + return i; +} + +static int etrax_usb_submit_bulk_urb(urb_t *urb) +{ + int epid; + int empty; + unsigned long flags; + + DBFENTER; + + /* Epid allocation, empty check and list add must be protected. + Read about this in etrax_usb_submit_ctrl_urb. */ + + spin_lock_irqsave(&urb_list_lock, flags); + epid = etrax_usb_setup_epid(urb); + if (epid == -1) { + DBFEXIT; + spin_unlock_irqrestore(&urb_list_lock, flags); + return -ENOMEM; + } + empty = urb_list_empty(epid); + urb_list_add(urb, epid); + spin_unlock_irqrestore(&urb_list_lock, flags); + + /* USB_QUEUE_BULK is UHCI-specific, but we warn anyway. */ + if (!(urb->transfer_flags & USB_QUEUE_BULK) && !empty) { + warn("USB_QUEUE_BULK is not set and urb queue is not empty, ignoring."); + } + + dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d", + usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid); + + /* Mark the urb as being in progress. */ + urb->status = -EINPROGRESS; + + if (empty) { + etrax_usb_add_to_bulk_sb_list(urb, epid); + } + + DBFEXIT; + + return 0; +} + +static void etrax_usb_add_to_bulk_sb_list(urb_t *urb, int epid) +{ + USB_SB_Desc_t *sb_desc; + etrax_urb_priv_t *urb_priv; + unsigned long flags; + char maxlen; + + DBFENTER; + + dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb); + + maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + + urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); + assert(urb_priv != NULL); + /* This sets rx_offset to 0. */ + memset(urb_priv, 0, sizeof(etrax_urb_priv_t)); + + sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); + assert(sb_desc != NULL); + memset(sb_desc, 0, sizeof(USB_SB_Desc_t)); + + + if (usb_pipeout(urb->pipe)) { + + dbg_bulk("Grabbing bulk OUT, urb 0x%lx, epid %d", (unsigned long)urb, epid); + + /* This is probably a sanity check of the bulk transaction length + not being larger than 64 kB. */ if (urb->transfer_buffer_length > 0xffff) { - panic(__FILE__ __FUNCTION__ ":urb->transfer_buffer_length > 0xffff\n"); + panic("urb->transfer_buffer_length > 0xffff"); } - sb_desc_1->sw_len = urb->transfer_buffer_length; /* was actual_length */ - sb_desc_1->command = IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, out) | + sb_desc->sw_len = urb->transfer_buffer_length; -#if 0 - IO_STATE(USB_SB_command, full, no) | -#else - IO_STATE(USB_SB_command, full, yes) | -#endif + /* The rem field is don't care if it's not a full-length transfer, so setting + it shouldn't hurt. Also, rem isn't used for OUT traffic. */ + sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) | + IO_STATE(USB_SB_command, tt, out) | + IO_STATE(USB_SB_command, eot, yes) | + IO_STATE(USB_SB_command, eol, yes)); + + /* The full field is set to yes, even if we don't actually check that this is + a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0). + Setting full prevents the USB controller from sending an empty packet in + that case. However, if USB_ZERO_PACKET was set we want that. */ + if (!(urb->transfer_flags & USB_ZERO_PACKET)) { + sb_desc->command |= IO_STATE(USB_SB_command, full, yes); + } - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes); - - dbg_bulk("transfer_buffer is at 0x%08X", urb->transfer_buffer); - - sb_desc_1->buf = virt_to_phys(urb->transfer_buffer); - sb_desc_1->next = 0; + sb_desc->buf = virt_to_phys(urb->transfer_buffer); + sb_desc->next = 0; } else if (usb_pipein(urb->pipe)) { - dbg_bulk("Transfer for epid %d is IN", epid); - dbg_bulk("transfer_buffer_length = %d", urb->transfer_buffer_length); - dbg_bulk("rem is calculated to %d", urb->transfer_buffer_length % maxlen); - - sb_desc_1->sw_len = urb->transfer_buffer_length ? + dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid); + + sb_desc->sw_len = urb->transfer_buffer_length ? (urb->transfer_buffer_length - 1) / maxlen + 1 : 0; - dbg_bulk("sw_len got %d", sb_desc_1->sw_len); - dbg_bulk("transfer_buffer is at 0x%08X", urb->transfer_buffer); - - sb_desc_1->command = - IO_FIELD(USB_SB_command, rem, - urb->transfer_buffer_length % maxlen) | - IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes); - sb_desc_1->buf = 0; - sb_desc_1->next = 0; + /* The rem field is don't care if it's not a full-length transfer, so setting + it shouldn't hurt. */ + sb_desc->command = + (IO_FIELD(USB_SB_command, rem, + urb->transfer_buffer_length % maxlen) | + IO_STATE(USB_SB_command, tt, in) | + IO_STATE(USB_SB_command, eot, yes) | + IO_STATE(USB_SB_command, eol, yes)); + + sb_desc->buf = 0; + sb_desc->next = 0; + } + + urb_priv->first_sb = sb_desc; + urb_priv->last_sb = sb_desc; + urb_priv->epid = epid; - urb_priv->rx_offset = 0; - urb_priv->eot = 0; - } - - urb_priv->first_sb = sb_desc_1; - - urb->hcpriv = (void *)urb_priv; + urb->hcpriv = urb_priv; - /* Reset toggle bits and reset error count, remeber to di and ei */ - /* Warning: it is possible that this locking doesn't work with bottom-halves */ - + /* Reset toggle bits and reset error count. */ save_flags(flags); cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop(); + *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); + nop(); + + /* FIXME: Is this a special case since the hold field is checked, + or should we check hold in a lot of other cases as well? */ if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) { - panic("Hold was set in %s\n", __FUNCTION__); + panic("Hold was set in %s", __FUNCTION__); } + /* Reset error counters (regardless of which direction this traffic is). */ *R_USB_EPT_DATA &= ~(IO_MASK(R_USB_EPT_DATA, error_count_in) | IO_MASK(R_USB_EPT_DATA, error_count_out)); + /* Software must preset the toggle bits. */ if (usb_pipeout(urb->pipe)) { char toggle = - usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out); *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle); } else { char toggle = - usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in); *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle); } - - /* Enable the EP descr. */ - - set_bit(epid, (void *)&ep_really_active); - - TxBulkEPList[epid].sub = virt_to_phys(sb_desc_1); + + /* Assert that the EP descriptor is disabled. */ + assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable))); + + /* The reason we set the EP's sub pointer directly instead of + walking the SB list and linking it last in the list is that we only + have one active urb at a time (the rest are queued). */ + + /* Note that we cannot have interrupts running when we have set the SB descriptor + but the EP is not yet enabled. If a bulk eot happens for another EP, we will + find this EP disabled and with a SB != 0, which will make us think that it's done. */ + TxBulkEPList[epid].sub = virt_to_phys(sb_desc); TxBulkEPList[epid].hw_len = 0; - TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); + /* Note that we don't have to fill in the ep_id field since this + was done when we allocated the EP descriptors in init_tx_bulk_ep. */ + + /* Check if the dummy list is already with us (if several urbs were queued). */ + if (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0])) { + + dbg_bulk("Inviting dummy list to the party for urb 0x%lx, epid %d", + (unsigned long)urb, epid); + + /* The last EP in the dummy list already has its next pointer set to + TxBulkEPList[epid].next. */ + /* We don't need to check if the DMA is at this EP or not before changing the + next pointer, since we will do it in one 32-bit write (EP descriptors are + 32-bit aligned). */ + TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]); + } + /* Enable the EP descr. */ + dbg_bulk("Enabling bulk EP for urb 0x%lx, epid %d", (unsigned long)urb, epid); + TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); + + /* Everything is set up, safe to enable interrupts again. */ restore_flags(flags); + /* If the DMA bulk channel isn't running, we need to restart it if it + has stopped at the last EP descriptor (DMA stopped because there was + no more traffic) or if it has stopped at a dummy EP with the intr flag + set (DMA stopped because we were too slow in inserting new traffic). */ if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) { - *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); - - } + USB_EP_Desc_t *ep; + ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP); + dbg_bulk("DMA channel not running in add"); + dbg_bulk("DMA is at 0x%lx", (unsigned long)ep); + + if (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[NBR_OF_EPIDS - 1]) || + (ep->command & 0x8) >> 3) { + *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); + /* Update/restart the bulk start timer since we just started the channel. */ + mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL); + /* Update/restart the bulk eot timer since we just inserted traffic. */ + mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL); + } + } + DBFEXIT; } -static void handle_bulk_transfer_attn(int epid, int status) +static void etrax_usb_complete_bulk_urb(urb_t *urb, int status) { - struct urb *old_urb; - etrax_urb_priv_t *hc_priv; - unsigned long flags; - + etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; + int epid = urb_priv->epid; + unsigned long flags; + DBFENTER; - clear_bit(epid, (void *)&ep_really_active); - - old_urb = URB_List[epid]; - URB_List[epid] = old_urb->next; + if (status) + warn("Completing bulk urb with status %d.", status); + + dbg_bulk("Completing bulk urb 0x%lx for epid %d", (unsigned long)urb, epid); + + /* Update the urb list. */ + urb_list_del(urb, epid); - /* if (status == 0 && IN) find data and copy to urb */ - if (status == 0 && usb_pipein(old_urb->pipe)) { - etrax_urb_priv_t *urb_priv; - - urb_priv = (etrax_urb_priv_t *)old_urb->hcpriv; - save_flags(flags); - cli(); - if (urb_priv->eot == 1) { - old_urb->actual_length = urb_priv->rx_offset; + /* For an IN pipe, we always set the actual length, regardless of whether there was + an error or not (which means the device driver can use the data if it wants to). */ + if (usb_pipein(urb->pipe)) { + urb->actual_length = urb_priv->rx_offset; + } else { + /* Set actual_length for OUT urbs also; the USB mass storage driver seems + to want that. We wouldn't know of any partial writes if there was an error. */ + if (status == 0) { + urb->actual_length = urb->transfer_buffer_length; } else { - if (urb_priv->rx_offset == 0) { - status = 0; - } else { - status = -EPROTO; - } - - old_urb->actual_length = 0; - err("(BULK) No eot set in IN data!!! rx_offset is: %d", urb_priv->rx_offset); + urb->actual_length = 0; } - - restore_flags(flags); } + /* FIXME: Is there something of the things below we shouldn't do if there was an error? + Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */ + save_flags(flags); cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop(); - if (usb_pipeout(old_urb->pipe)) { + *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); + nop(); + + /* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */ + if (usb_pipeout(urb->pipe)) { char toggle = - IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA); - usb_settoggle(old_urb->dev, usb_pipeendpoint(old_urb->pipe), - usb_pipeout(old_urb->pipe), toggle); + IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA); + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), toggle); } else { char toggle = - IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA); - usb_settoggle(old_urb->dev, usb_pipeendpoint(old_urb->pipe), - usb_pipeout(old_urb->pipe), toggle); + IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA); + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), toggle); + } + restore_flags(flags); + + /* Remember to free the SBs. */ + etrax_remove_from_sb_list(urb); + kfree(urb_priv); + urb->hcpriv = 0; + + /* If there are any more urb's in the list we'd better start sending */ + if (!urb_list_empty(epid)) { + + urb_t *new_urb; + + /* Get the first urb. */ + new_urb = urb_list_first(epid); + assert(new_urb); + + dbg_bulk("More bulk for epid %d", epid); + + etrax_usb_add_to_bulk_sb_list(new_urb, epid); + } + + urb->status = status; + + /* We let any non-zero status from the layer above have precedence. */ + if (status == 0) { + /* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length) + is to be treated as an error. */ + if (urb->transfer_flags & USB_DISABLE_SPD) { + if (usb_pipein(urb->pipe) && + (urb->actual_length != + usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) { + urb->status = -EREMOTEIO; + } + } + } + + if (urb->complete) { + urb->complete(urb); } - restore_flags(flags); - - /* If there are any more URB's in the list we'd better start sending */ - if (URB_List[epid]) { - etrax_usb_do_bulk_hw_add(URB_List[epid], epid, - usb_maxpacket(URB_List[epid]->dev, URB_List[epid]->pipe, - usb_pipeout(URB_List[epid]->pipe))); - } -#if 1 - else { - /* This means that this EP is now free, deconfigure it */ + if (urb_list_empty(epid)) { + /* This means that this EP is now free, deconfigure it. */ etrax_usb_free_epid(epid); - } -#endif - - /* Remember to free the SB's */ - hc_priv = (etrax_urb_priv_t *)old_urb->hcpriv; - cleanup_sb(hc_priv->first_sb); - kfree(hc_priv); + + /* No more traffic; time to clean up. + Must set sub pointer to 0, since we look at the sub pointer when handling + the bulk eot interrupt. */ + + dbg_bulk("No bulk for epid %d", epid); + + TxBulkEPList[epid].sub = 0; + + /* Unlink the dummy list. */ - old_urb->status = status; - if (old_urb->complete) { - old_urb->complete(old_urb); + dbg_bulk("Kicking dummy list out of party for urb 0x%lx, epid %d", + (unsigned long)urb, epid); + + /* No need to wait for the DMA before changing the next pointer. + The modulo NBR_OF_EPIDS isn't actually necessary, since we will never use + the last one (INVALID_EPID) for actual traffic. */ + TxBulkEPList[epid].next = + virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]); } DBFEXIT; } -/* ---------------------------------------------------------------------------- */ - -static int etrax_usb_submit_ctrl_urb(struct urb *urb) +static int etrax_usb_submit_ctrl_urb(urb_t *urb) { int epid; - char devnum; - char endpoint; - char maxlen; - char out_traffic; - char slow; - - struct urb *tmp_urb; - - unsigned long flags; - + int empty; + unsigned long flags; + DBFENTER; - devnum = usb_pipedevice(urb->pipe); - endpoint = usb_pipeendpoint(urb->pipe); - maxlen = usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe)); - out_traffic = usb_pipeout(urb->pipe); - slow = usb_pipeslow(urb->pipe); - - epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic); + /* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */ + + /* Epid allocation, empty check and list add must be protected. + + Epid allocation because if we find an existing epid for this endpoint an urb might be + completed (emptying the list) before we add the new urb to the list, causing the epid + to be de-allocated. We would then start the transfer with an invalid epid -> epid attn. + + Empty check and add because otherwise we might conclude that the list is not empty, + after which it becomes empty before we add the new urb to the list, causing us not to + insert the new traffic into the SB list. */ + + spin_lock_irqsave(&urb_list_lock, flags); + epid = etrax_usb_setup_epid(urb); if (epid == -1) { - epid = etrax_usb_allocate_epid(); - if (epid == -1) { - /* We're out of endpoints, return some error */ - err("We're out of endpoints"); - return -ENOMEM; - } - /* Now we have to fill in this ep */ - etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic); + spin_unlock_irqrestore(&urb_list_lock, flags); + DBFEXIT; + return -ENOMEM; } - /* Ok, now we got valid endpoint, lets insert some traffic */ + empty = urb_list_empty(epid); + urb_list_add(urb, epid); + spin_unlock_irqrestore(&urb_list_lock, flags); + dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d", + (unsigned long)urb, empty ? "empty" : "", epid); + + /* Mark the urb as being in progress. */ urb->status = -EINPROGRESS; - save_flags(flags); - cli(); - - if (URB_List[epid]) { - /* Find end of list and add */ - for (tmp_urb = URB_List[epid]; tmp_urb->next; tmp_urb = tmp_urb->next) - dump_urb(tmp_urb); - - tmp_urb->next = urb; - restore_flags(flags); - } else { - /* If this is the first URB, add the URB and do HW add */ - URB_List[epid] = urb; - restore_flags(flags); - etrax_usb_do_ctrl_hw_add(urb, epid, maxlen); - } + if (empty) { + etrax_usb_add_to_ctrl_sb_list(urb, epid); + } DBFEXIT; return 0; } -static void etrax_usb_do_ctrl_hw_add(struct urb *urb, int epid, char maxlen) +static void etrax_usb_add_to_ctrl_sb_list(urb_t *urb, int epid) { - USB_SB_Desc_t *sb_desc_1; - USB_SB_Desc_t *sb_desc_2; - USB_SB_Desc_t *sb_desc_3; + USB_SB_Desc_t *sb_desc_setup; + USB_SB_Desc_t *sb_desc_data; + USB_SB_Desc_t *sb_desc_status; etrax_urb_priv_t *urb_priv; unsigned long flags; - + char maxlen; + DBFENTER; - urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_KERNEL); - sb_desc_1 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL); - sb_desc_2 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL); - - if (!(sb_desc_1 && sb_desc_2)) { - panic("kmem_cache_alloc in ctrl_hw_add gave NULL pointers !!!\n"); - } - - sb_desc_1->sw_len = 8; - sb_desc_1->command = IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, setup) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes); - - sb_desc_1->buf = virt_to_phys(urb->setup_packet); - sb_desc_1->next = virt_to_phys(sb_desc_2); - dump_sb_desc(sb_desc_1); + maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + + urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); + assert(urb_priv != NULL); + /* This sets rx_offset to 0. */ + memset(urb_priv, 0, sizeof(etrax_urb_priv_t)); + + sb_desc_setup = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); + assert(sb_desc_setup != NULL); + sb_desc_status = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); + assert(sb_desc_status != NULL); + + /* Initialize the mandatory setup SB descriptor (used only in control transfers) */ + sb_desc_setup->sw_len = 8; + sb_desc_setup->command = (IO_FIELD(USB_SB_command, rem, 0) | + IO_STATE(USB_SB_command, tt, setup) | + IO_STATE(USB_SB_command, full, yes) | + IO_STATE(USB_SB_command, eot, yes)); + + sb_desc_setup->buf = virt_to_phys(urb->setup_packet); if (usb_pipeout(urb->pipe)) { - dbg_ctrl("Transfer for epid %d is OUT", epid); + dbg_ctrl("Transfer for epid %d is OUT", epid); /* If this Control OUT transfer has an optional data stage we add an OUT token before the mandatory IN (status) token, hence the reordered SB list */ + sb_desc_setup->next = virt_to_phys(sb_desc_status); if (urb->transfer_buffer) { - dbg_ctrl("This OUT transfer has an extra data stage"); - sb_desc_3 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL); - sb_desc_1->next = virt_to_phys(sb_desc_3); + dbg_ctrl("This OUT transfer has an extra data stage"); - sb_desc_3->sw_len = urb->transfer_buffer_length; - sb_desc_3->command = IO_STATE(USB_SB_command, tt, out) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes); - sb_desc_3->buf = virt_to_phys(urb->transfer_buffer); - sb_desc_3->next = virt_to_phys(sb_desc_2); - } - - sb_desc_2->sw_len = 1; - sb_desc_2->command = IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes); - - sb_desc_2->buf = 0; - sb_desc_2->next = 0; - dump_sb_desc(sb_desc_2); - + sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); + assert(sb_desc_data != NULL); + + sb_desc_setup->next = virt_to_phys(sb_desc_data); + + sb_desc_data->sw_len = urb->transfer_buffer_length; + sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) | + IO_STATE(USB_SB_command, full, yes) | + IO_STATE(USB_SB_command, eot, yes)); + sb_desc_data->buf = virt_to_phys(urb->transfer_buffer); + sb_desc_data->next = virt_to_phys(sb_desc_status); + } + + sb_desc_status->sw_len = 1; + sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) | + IO_STATE(USB_SB_command, tt, in) | + IO_STATE(USB_SB_command, eot, yes) | + IO_STATE(USB_SB_command, eol, yes)); + + sb_desc_status->buf = 0; + sb_desc_status->next = 0; + } else if (usb_pipein(urb->pipe)) { dbg_ctrl("Transfer for epid %d is IN", epid); dbg_ctrl("transfer_buffer_length = %d", urb->transfer_buffer_length); dbg_ctrl("rem is calculated to %d", urb->transfer_buffer_length % maxlen); - sb_desc_3 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL); + sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); + assert(sb_desc_data != NULL); - sb_desc_2->sw_len = urb->transfer_buffer_length ? + sb_desc_setup->next = virt_to_phys(sb_desc_data); + + sb_desc_data->sw_len = urb->transfer_buffer_length ? (urb->transfer_buffer_length - 1) / maxlen + 1 : 0; - dbg_ctrl("sw_len got %d", sb_desc_2->sw_len); + dbg_ctrl("sw_len got %d", sb_desc_data->sw_len); - sb_desc_2->command = - IO_FIELD(USB_SB_command, rem, - urb->transfer_buffer_length % maxlen) | - IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes); - - sb_desc_2->buf = 0; - sb_desc_2->next = virt_to_phys(sb_desc_3); - dump_sb_desc(sb_desc_2); - - sb_desc_3->sw_len = 1; - sb_desc_3->command = IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, zout) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes); + sb_desc_data->command = + (IO_FIELD(USB_SB_command, rem, + urb->transfer_buffer_length % maxlen) | + IO_STATE(USB_SB_command, tt, in) | + IO_STATE(USB_SB_command, eot, yes)); + + sb_desc_data->buf = 0; + sb_desc_data->next = virt_to_phys(sb_desc_status); + + /* Read comment at zout_buffer declaration for an explanation to this. */ + sb_desc_status->sw_len = 1; + sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) | + IO_STATE(USB_SB_command, tt, zout) | + IO_STATE(USB_SB_command, full, yes) | + IO_STATE(USB_SB_command, eot, yes) | + IO_STATE(USB_SB_command, eol, yes)); - sb_desc_3->buf = 0; - sb_desc_3->next = 0; - dump_sb_desc(sb_desc_3); - - urb_priv->rx_offset = 0; - urb_priv->eot = 0; + sb_desc_status->buf = virt_to_phys(&zout_buffer[0]); + sb_desc_status->next = 0; } - urb_priv->first_sb = sb_desc_1; - - urb->hcpriv = (void *)urb_priv; + urb_priv->first_sb = sb_desc_setup; + urb_priv->last_sb = sb_desc_status; + urb_priv->epid = epid; + + urb->hcpriv = urb_priv; /* Reset toggle bits and reset error count, remeber to di and ei */ /* Warning: it is possible that this locking doesn't work with bottom-halves */ @@ -1332,661 +2181,1059 @@ save_flags(flags); cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop(); + *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); + nop(); if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) { - panic("Hold was set in %s\n", __FUNCTION__); + panic("Hold was set in %s", __FUNCTION__); } + /* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits + are set to a specific value. Why the difference? Read "Transfer and Toggle Bits + in Designer's Reference, p. 8 - 11. */ *R_USB_EPT_DATA &= ~(IO_MASK(R_USB_EPT_DATA, error_count_in) | IO_MASK(R_USB_EPT_DATA, error_count_out) | IO_MASK(R_USB_EPT_DATA, t_in) | IO_MASK(R_USB_EPT_DATA, t_out)); - /* Enable the EP descr. */ + /* Since we use the rx interrupt to complete ctrl urbs, we can enable interrupts now + (i.e. we don't check the sub pointer on an eot interrupt like we do for bulk traffic). */ + restore_flags(flags); - set_bit(epid, (void *)&ep_really_active); - - TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_1); + /* Assert that the EP descriptor is disabled. */ + assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable))); + + /* Set up and enable the EP descriptor. */ + TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_setup); TxCtrlEPList[epid].hw_len = 0; TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - restore_flags(flags); - - dump_ep_desc(&TxCtrlEPList[epid]); - - if (!(*R_DMA_CH8_SUB1_CMD & IO_MASK(R_DMA_CH8_SUB1_CMD, cmd))) { - *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start); - - } + /* We start the DMA sub channel without checking if it's running or not, because: + 1) If it's already running, issuing the start command is a nop. + 2) We avoid a test-and-set race condition. */ + *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start); DBFEXIT; } -static int etrax_usb_submit_urb(struct urb *urb) +static void etrax_usb_complete_ctrl_urb(urb_t *urb, int status) { - etrax_hc_t *hc; - int rval = -EINVAL; - - DBFENTER; + etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; + int epid = urb_priv->epid; - urb->next = NULL; + DBFENTER; - dump_urb(urb); - submit_urb_count++; - - hc = (etrax_hc_t*) urb->dev->bus->hcpriv; - - if (usb_pipedevice(urb->pipe) == hc->rh.devnum) { - /* This request if for the Virtual Root Hub */ - rval = etrax_rh_submit_urb(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - rval = etrax_usb_submit_ctrl_urb(urb); + if (status) + warn("Completing ctrl urb with status %d.", status); - } else if (usb_pipetype(urb->pipe) == PIPE_BULK) { - rval = etrax_usb_submit_bulk_urb(urb); + dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb); - } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { - int bustime; + /* Remove this urb from the list. */ + urb_list_del(urb, epid); - if (urb->bandwidth == 0) { - bustime = usb_check_bandwidth(urb->dev, urb); - if (bustime < 0) { - rval = bustime; - } else { - usb_claim_bandwidth(urb->dev, urb, bustime, 0); - rval = etrax_usb_submit_intr_urb(urb); - } - - } - } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - warn("Isochronous traffic is not supported !!!"); - rval = -EINVAL; + /* For an IN pipe, we always set the actual length, regardless of whether there was + an error or not (which means the device driver can use the data if it wants to). */ + if (usb_pipein(urb->pipe)) { + urb->actual_length = urb_priv->rx_offset; } - DBFEXIT; + /* FIXME: Is there something of the things below we shouldn't do if there was an error? + Like, maybe we shouldn't insert more traffic. */ - return rval; -} + /* Remember to free the SBs. */ + etrax_remove_from_sb_list(urb); + kfree(urb_priv); + urb->hcpriv = 0; + + /* If there are any more urbs in the list we'd better start sending. */ + if (!urb_list_empty(epid)) { + urb_t *new_urb; + + /* Get the first urb. */ + new_urb = urb_list_first(epid); + assert(new_urb); -static int etrax_usb_unlink_urb(struct urb *urb) -{ - etrax_hc_t *hc = urb->dev->bus->hcpriv; - int epid; - int devnum, endpoint, slow, maxlen, out_traffic; - etrax_urb_priv_t *hc_priv; - unsigned long flags; - - DBFENTER; - dump_urb(urb); - devnum = usb_pipedevice(urb->pipe); - endpoint = usb_pipeendpoint(urb->pipe); - slow = usb_pipeslow(urb->pipe); - maxlen = usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe)); - out_traffic = usb_pipeout(urb->pipe); - - epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic); + dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb); - if (epid == -1) - return 0; - - if (usb_pipedevice(urb->pipe) == hc->rh.devnum) { - int ret; - ret = etrax_rh_unlink_urb(urb); - DBFEXIT; - return ret; - } else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) { - int ret; - ret = etrax_usb_unlink_intr_urb(urb); - urb->status = -ENOENT; - if (urb->complete) { - urb->complete(urb); - } - DBFEXIT; - return ret; + etrax_usb_add_to_ctrl_sb_list(new_urb, epid); } - - info("Unlink of BULK or CTRL"); - - save_flags(flags); - cli(); - for (epid = 0; epid < 32; epid++) { - struct urb *u = URB_List[epid]; - struct urb *prev = NULL; - int pos = 0; - - for (; u; u = u->next) { - pos++; - if (u == urb) { - if (!prev) { - URB_List[epid] = u->next; - } else { - prev->next = u->next; - } + urb->status = status; - restore_flags(flags); - - if (!prev) { - if (usb_pipetype(u->pipe) == PIPE_CONTROL) { - if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) { - /* The EP was enabled, disable it and wait */ - TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); - while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid])); - } - } else if (usb_pipetype(u->pipe) == PIPE_BULK) { - if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) { - TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); - while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid])); - } - } - } - - info("Found urb at epid %d, pos %d", epid, pos); - - u->status = -ENOENT; - if (u->complete) { - u->complete(u); - } - - hc_priv = (etrax_urb_priv_t *)u->hcpriv; - cleanup_sb(hc_priv->first_sb); - kfree(hc_priv); + /* We let any non-zero status from the layer above have precedence. */ + if (status == 0) { + /* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length) + is to be treated as an error. */ + if (urb->transfer_flags & USB_DISABLE_SPD) { + if (usb_pipein(urb->pipe) && + (urb->actual_length != + usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) { + urb->status = -EREMOTEIO; + } + } + } + + if (urb->complete) { + urb->complete(urb); + } - DBFEXIT; - return 0; - } - prev = u; - } + if (urb_list_empty(epid)) { + /* No more traffic. Time to clean up. */ + etrax_usb_free_epid(epid); + /* Must set sub pointer to 0. */ + dbg_ctrl("No ctrl for epid %d", epid); + TxCtrlEPList[epid].sub = 0; } - restore_flags(flags); - DBFEXIT; - return 0; } -static int etrax_usb_get_frame_number(struct usb_device *usb_dev) +static int etrax_usb_submit_intr_urb(urb_t *urb) { + + int epid; + DBFENTER; - DBFEXIT; - return (*R_USB_FM_NUMBER); -} + + if (usb_pipeout(urb->pipe)) { + /* Unsupported transfer type. + We don't support interrupt out traffic. (If we do, we can't support + intervals for neither in or out traffic, but are forced to schedule all + interrupt traffic in one frame.) */ + return -EINVAL; + } -static int etrax_usb_allocate_dev(struct usb_device *usb_dev) -{ - DBFENTER; - DBFEXIT; - return 0; -} + epid = etrax_usb_setup_epid(urb); + if (epid == -1) { + DBFEXIT; + return -ENOMEM; + } -static int etrax_usb_deallocate_dev(struct usb_device *usb_dev) -{ - DBFENTER; - DBFEXIT; - return 0; -} + if (!urb_list_empty(epid)) { + /* There is already a queued urb for this endpoint. */ + etrax_usb_free_epid(epid); + return -ENXIO; + } + + urb->status = -EINPROGRESS; -static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs) -{ - DBFENTER; + dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid); - if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) { - info("dma8_sub0_descr (BULK) intr."); - *R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do); - } - if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) { - info("dma8_sub1_descr (CTRL) intr."); - *R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do); - } - if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) { - info("dma8_sub2_descr (INT) intr."); - *R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do); - } - if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) { - info("dma8_sub3_descr (ISO) intr."); - *R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do); - } + urb_list_add(urb, epid); + etrax_usb_add_to_intr_sb_list(urb, epid); + + return 0; DBFEXIT; } -static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs) +static void etrax_usb_add_to_intr_sb_list(urb_t *urb, int epid) { - int epid = 0; - struct urb *urb; + + volatile USB_EP_Desc_t *tmp_ep; + volatile USB_EP_Desc_t *first_ep; + + char maxlen; + int interval; + int i; + etrax_urb_priv_t *urb_priv; - - *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do); + + DBFENTER; - while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) { - if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) { + maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + interval = urb->interval; - goto skip_out; - } + urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); + assert(urb_priv != NULL); + memset(urb_priv, 0, sizeof(etrax_urb_priv_t)); + urb->hcpriv = urb_priv; - if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) { - - goto skip_out; - } - - epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status); + first_ep = &TxIntrEPList[0]; - urb = URB_List[epid]; + /* Round of the interval to 2^n, it is obvious that this code favours + smaller numbers, but that is actually a good thing */ + /* FIXME: The "rounding error" for larger intervals will be quite + large. For in traffic this shouldn't be a problem since it will only + mean that we "poll" more often. */ + for (i = 0; interval; i++) { + interval = interval >> 1; + } + interval = 1 << (i - 1); - if (urb && usb_pipein(urb->pipe)) { - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; + dbg_intr("Interval rounded to %d", interval); - if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { - struct in_chunk *in; - dbg_intr("Packet for epid %d in rx buffers", epid); - in = kmalloc(sizeof(struct in_chunk), GFP_ATOMIC); - in->length = myNextRxDesc->hw_len; - in->data = kmalloc(in->length, GFP_ATOMIC); - memcpy(in->data, phys_to_virt(myNextRxDesc->buf), in->length); - list_add_tail(&in->list, &urb_priv->ep_in_list); -#ifndef ETRAX_USB_INTR_IRQ - etrax_usb_hc_intr_top_half(irq, vhc, regs); -#endif - - } else { - if ((urb_priv->rx_offset + myNextRxDesc->hw_len) > - urb->transfer_buffer_length) { - err("Packet (epid: %d) in RX buffer (%d) was bigger " - "than the URB has room for (%d)!!!", epid, urb_priv->rx_offset + myNextRxDesc->hw_len, urb->transfer_buffer_length); - goto skip_out; - } + tmp_ep = first_ep; + i = 0; + do { + if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) { + if ((i % interval) == 0) { + /* Insert the traffic ep after tmp_ep */ + USB_EP_Desc_t *ep_desc; + USB_SB_Desc_t *sb_desc; + + dbg_intr("Inserting EP for epid %d", epid); + + ep_desc = (USB_EP_Desc_t *) + kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); + sb_desc = (USB_SB_Desc_t *) + kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); + assert(ep_desc != NULL); + CHECK_ALIGN(ep_desc); + assert(sb_desc != NULL); - memcpy(urb->transfer_buffer + urb_priv->rx_offset, - phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len); + ep_desc->sub = virt_to_phys(sb_desc); + ep_desc->hw_len = 0; + ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) | + IO_STATE(USB_EP_command, enable, yes)); + + + /* Round upwards the number of packets of size maxlen + that this SB descriptor should receive. */ + sb_desc->sw_len = urb->transfer_buffer_length ? + (urb->transfer_buffer_length - 1) / maxlen + 1 : 0; + sb_desc->next = 0; + sb_desc->buf = 0; + sb_desc->command = + (IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) | + IO_STATE(USB_SB_command, tt, in) | + IO_STATE(USB_SB_command, eot, yes) | + IO_STATE(USB_SB_command, eol, yes)); - urb_priv->rx_offset += myNextRxDesc->hw_len; - } - - if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) { - urb_priv->eot = 1; + ep_desc->next = tmp_ep->next; + tmp_ep->next = virt_to_phys(ep_desc); } - - } else { - err("This is almost fatal, inpacket for epid %d which does not exist " - " or is out!!!\nURB was at 0x%08lX", epid, (unsigned long)urb); - - goto skip_out; + i++; } + tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next); + } while (tmp_ep != first_ep); - skip_out: - myPrevRxDesc = myNextRxDesc; - myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol); - myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol); - myLastRxDesc = myPrevRxDesc; - myNextRxDesc->status = 0; - myNextRxDesc = phys_to_virt(myNextRxDesc->next); - } + /* Note that first_sb/last_sb doesn't apply to interrupt traffic. */ + urb_priv->epid = epid; + + /* We start the DMA sub channel without checking if it's running or not, because: + 1) If it's already running, issuing the start command is a nop. + 2) We avoid a test-and-set race condition. */ + *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start); + + DBFEXIT; } -static void cleanup_sb(USB_SB_Desc_t *sb) +static void etrax_usb_complete_intr_urb(urb_t *urb, int status) { - USB_SB_Desc_t *next_sb; - + etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; + int epid = urb_priv->epid; + DBFENTER; - if (sb == NULL) { - err("cleanup_sb was given a NULL pointer"); - return; - } + if (status) + warn("Completing intr urb with status %d.", status); - while (!(sb->command & IO_MASK(USB_SB_command, eol))) { - next_sb = (USB_SB_Desc_t *)phys_to_virt(sb->next); - kmem_cache_free(usb_desc_cache, sb); - sb = next_sb; - } + dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb); - kmem_cache_free(usb_desc_cache, sb); + urb->status = status; + urb->actual_length = urb_priv->rx_offset; - DBFEXIT; + dbg_intr("interrupt urb->actual_length = %d", urb->actual_length); + /* We let any non-zero status from the layer above have precedence. */ + if (status == 0) { + /* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length) + is to be treated as an error. */ + if (urb->transfer_flags & USB_DISABLE_SPD) { + if (urb->actual_length != + usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) { + urb->status = -EREMOTEIO; + } + } + } + + if (urb->complete) { + urb->complete(urb); + } + + /* Device driver has taken care of the data now. */ + urb_priv->rx_offset = 0; + + DBFEXIT; } -static void handle_control_transfer_attn(int epid, int status) + +static int etrax_usb_submit_isoc_urb(urb_t *urb) { - struct urb *old_urb; - etrax_urb_priv_t *hc_priv; + int epid; DBFENTER; - clear_bit(epid, (void *)&ep_really_active); - - old_urb = URB_List[epid]; - URB_List[epid] = old_urb->next; - - /* if (status == 0 && IN) find data and copy to urb */ - if (status == 0 && usb_pipein(old_urb->pipe)) { - unsigned long flags; - etrax_urb_priv_t *urb_priv; - - urb_priv = (etrax_urb_priv_t *)old_urb->hcpriv; - save_flags(flags); - cli(); - if (urb_priv->eot == 1) { - old_urb->actual_length = urb_priv->rx_offset; - dbg_ctrl("urb_priv->rx_offset: %d in handle_control_attn", urb_priv->rx_offset); - } else { - status = -EPROTO; - old_urb->actual_length = 0; - err("(CTRL) No eot set in IN data!!! rx_offset: %d", urb_priv->rx_offset); - } - - restore_flags(flags); - } - - /* If there are any more URB's in the list we'd better start sending */ - if (URB_List[epid]) { - etrax_usb_do_ctrl_hw_add(URB_List[epid], epid, - usb_maxpacket(URB_List[epid]->dev, URB_List[epid]->pipe, - usb_pipeout(URB_List[epid]->pipe))); - } -#if 1 - else { - /* This means that this EP is now free, deconfigure it */ - etrax_usb_free_epid(epid); + /* Is there an active epid for this urb ? */ + epid = etrax_usb_setup_epid(urb); + if (epid == -1) { + DBFEXIT; + return -ENOMEM; } -#endif - /* Remember to free the SB's */ - hc_priv = (etrax_urb_priv_t *)old_urb->hcpriv; - cleanup_sb(hc_priv->first_sb); - kfree(hc_priv); + dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb); - old_urb->status = status; - if (old_urb->complete) { - old_urb->complete(old_urb); - } + /* Ok, now we got valid endpoint, lets insert some traffic */ - DBFEXIT; -} + urb->status = -EINPROGRESS; + /* Find the last urb in the URB_List and add this urb after that one. + Also add the traffic, that is do an isoc_hw_add. This is important + to make this in "real time" since isochronous traffic is time sensitive. */ + + dbg_isoc("Adding isoc urb to (possibly empty) list"); + urb_list_add(urb, epid); + etrax_usb_add_to_isoc_sb_list(urb, epid); + + DBFEXIT; + return 0; +} -static void etrax_usb_hc_intr_bottom_half(void *data) +static void etrax_usb_add_to_isoc_sb_list(urb_t *urb, int epid) { - struct usb_reg_context *reg = (struct usb_reg_context *)data; + + int i = 0; - int error_code; - int epid; + etrax_urb_priv_t *urb_priv; + USB_SB_Desc_t *prev_sb_desc, *next_sb_desc, *temp_sb_desc; - __u32 r_usb_ept_data; + DBFENTER; - etrax_hc_t *hc = reg->hc; - __u16 r_usb_rh_port_status_1; - __u16 r_usb_rh_port_status_2; + prev_sb_desc = next_sb_desc = temp_sb_desc = NULL; - DBFENTER; + urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); + assert(urb_priv != NULL); + memset(urb_priv, 0, sizeof(etrax_urb_priv_t)); - if (reg->r_usb_irq_mask_read & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) { + urb->hcpriv = urb_priv; + urb_priv->epid = epid; + + if (usb_pipeout(urb->pipe)) { - /* - The Etrax RH does not include a wPortChange register, so this has - to be handled in software. See section 11.16.2.6.2 in USB 1.1 spec - for details. - */ + /* Not implemented yet! */ + dbg_isoc("Transfer for epid %d is OUT", epid); - r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1; - r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2; + /* Create one SB descriptor for each packet and link them together. */ + for (i = 0; i < urb->number_of_packets; i++) { - dbg_rh("port_status pending"); - dbg_rh("r_usb_rh_port_status_1: 0x%04X", r_usb_rh_port_status_1); - dbg_rh("r_usb_rh_port_status_2: 0x%04X", r_usb_rh_port_status_2); + next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); + assert(next_sb_desc != NULL); + + next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) | + IO_STATE(USB_SB_command, eot, yes)); + + next_sb_desc->sw_len = urb->iso_frame_desc[i].length; + next_sb_desc->buf = virt_to_phys(urb->transfer_buffer + urb->iso_frame_desc[i].offset); + + /* First SB descriptor that belongs to this urb */ + if (i == 0) + urb_priv->first_sb = next_sb_desc; + else + prev_sb_desc->next = virt_to_phys(next_sb_desc); + + prev_sb_desc = next_sb_desc; + } + /* Check if full length transfer. */ + if (urb->iso_frame_desc[urb->number_of_packets - 1].length == + usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) { + next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes); + } - /* C_PORT_CONNECTION is set on any transition */ - hc->rh.wPortChange_1 |= - ((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) != - (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ? - (1 << RH_PORT_CONNECTION) : 0; - - hc->rh.wPortChange_2 |= - ((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) != - (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ? - (1 << RH_PORT_CONNECTION) : 0; + next_sb_desc->command |= IO_STATE(USB_SB_command, eol, yes); + next_sb_desc->next = 0; + urb_priv->last_sb = next_sb_desc; + + } else if (usb_pipein(urb->pipe)) { - /* C_PORT_ENABLE is _only_ set on a one to zero transition */ - hc->rh.wPortChange_1 |= - ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE)) - && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ? - (1 << RH_PORT_ENABLE) : 0; - - hc->rh.wPortChange_2 |= - ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE)) - && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ? - (1 << RH_PORT_ENABLE) : 0; - - /* C_PORT_SUSPEND seems difficult, lets ignore it.. (for now) */ - - /* C_PORT_RESET is _only_ set on a transition from the resetting state - to the enabled state */ - hc->rh.wPortChange_1 |= - ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET)) - && (r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ? - (1 << RH_PORT_RESET) : 0; - - hc->rh.wPortChange_2 |= - ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET)) - && (r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ? - (1 << RH_PORT_RESET) : 0; - - hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1; - hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2; + dbg_isoc("Transfer for epid %d is IN", epid); + dbg_isoc("transfer_buffer_length = %d", urb->transfer_buffer_length); + dbg_isoc("rem is calculated to %d", urb->iso_frame_desc[urb->number_of_packets - 1].length); + + /* Note that in descriptors for periodic traffic are not consumed. This means that + the USB controller never propagates in the SB list. In other words, if there already + is an SB descriptor in the list for this EP we don't have to do anything. */ + if (TxIsocEPList[epid].sub == 0) { + dbg_isoc("Isoc traffic not already running, allocating SB"); + + next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); + assert(next_sb_desc != NULL); + + next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) | + IO_STATE(USB_SB_command, eot, yes) | + IO_STATE(USB_SB_command, eol, yes)); + + next_sb_desc->sw_len = urb->number_of_packets; + next_sb_desc->buf = 0; + + /* The rem field is don't care for isoc traffic, so we don't set it. */ + + /* Only one SB descriptor that belongs to this urb. */ + urb_priv->first_sb = next_sb_desc; + urb_priv->last_sb = next_sb_desc; + + } else { + + dbg_isoc("Isoc traffic already running, just setting first/last_sb"); + + /* Each EP for isoc in will have only one SB descriptor, setup when submitting the + already active urb. Note that even though we may have several first_sb/last_sb + pointing at the same SB descriptor, they are freed only once (when the list has + become empty). */ + urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub); + urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub); + return; + } + + } + + /* Find the spot to insert this urb and add it. */ + if (TxIsocEPList[epid].sub == 0) { + /* First SB descriptor inserted in this list (in or out). */ + dbg_isoc("Inserting SB desc first in list"); + TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb); + } else { + /* Isochronous traffic is already running, insert new traffic last (only out). */ + dbg_isoc("Inserting SB desc last in list"); + temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub); + while (!(temp_sb_desc->command & IO_MASK(USB_SB_command, eol))) { + temp_sb_desc = phys_to_virt(temp_sb_desc->next); + } + /* Next pointer must be set before eol is removed. */ + temp_sb_desc->next = virt_to_phys(urb_priv->first_sb); + /* Clear the previous end of list flag since there is a new in the + added SB descriptor list. */ + temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol); + } + + if (urb->transfer_flags & USB_ISO_ASAP) { + /* The isoc transfer should be started as soon as possible. The start_frame + field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER + with a USB Chief trace shows that the first isoc IN token is sent 2 frames + later. I'm not sure how this affects usage of the start_frame field by the + device driver, or how it affects things when USB_ISO_ASAP is not set, so + therefore there's no compensation for the 2 frame "lag" here. */ + urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff); + TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); + urb_priv->urb_state = STARTED; + dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame); + } else { + /* Not started yet. */ + urb_priv->urb_state = NOT_STARTED; + } + + /* We start the DMA sub channel without checking if it's running or not, because: + 1) If it's already running, issuing the start command is a nop. + 2) We avoid a test-and-set race condition. */ + *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start); + + DBFEXIT; +} + +static void etrax_usb_complete_isoc_urb(urb_t *urb, int status) +{ + etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; + int epid = urb_priv->epid; + + DBFENTER; + + if (status) + warn("Completing isoc urb with status %d.", status); + + if (usb_pipein(urb->pipe)) { + + /* Move this one down the list. */ + urb_list_move_last(urb, epid); + + /* Mark the now first urb as started (may already be). */ + ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED; } - for (epid = 0; epid < 32; epid++) { + urb->status = status; + if (urb->complete) { + urb->complete(urb); + } - unsigned long flags; + DBFEXIT; +} - save_flags(flags); - cli(); +static void etrax_usb_complete_urb(urb_t *urb, int status) +{ + switch (usb_pipetype(urb->pipe)) { + case PIPE_BULK: + etrax_usb_complete_bulk_urb(urb, status); + break; + case PIPE_CONTROL: + etrax_usb_complete_ctrl_urb(urb, status); + break; + case PIPE_INTERRUPT: + etrax_usb_complete_intr_urb(urb, status); + break; + case PIPE_ISOCHRONOUS: + etrax_usb_complete_isoc_urb(urb, status); + break; + default: + err("Unknown pipetype"); + } +} - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop(); - r_usb_ept_data = *R_USB_EPT_DATA; - restore_flags(flags); - if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) { - warn("Was hold for epid %d", epid); - continue; - } +static void etrax_usb_hc_interrupt_top_half(int irq, void *vhc, struct pt_regs *regs) +{ + usb_interrupt_registers_t *reg; + unsigned long flags; + __u32 irq_mask; + __u8 status; + __u32 epid_attn; + __u16 port_status_1; + __u16 port_status_2; + __u32 fm_number; - if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) { - continue; - } - - - if (test_bit(epid, (void *)®->r_usb_epid_attn)) { + DBFENTER; - if (URB_List[epid] == NULL) { - err("R_USB_EPT_DATA is 0x%08X", r_usb_ept_data); - err("submit urb has been called %lu times..", submit_urb_count); - err("EPID_ATTN for epid %d, with NULL entry in list", epid); - return; - } - - dbg_ep("r_usb_ept_data [%d] == 0x%08X", epid, - r_usb_ept_data); - - error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, - r_usb_ept_data); - - if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { - /* no_error means that this urb was sucessfully sent or that we have - some undefinde error*/ - - if (IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3 || - IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3) { - /* Actually there were transmission errors */ - warn("Undefined error for epid %d", epid); - if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) { - handle_control_transfer_attn(epid, -EPROTO); - } else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) { - handle_bulk_transfer_attn(epid, -EPROTO); - } else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) { - handle_intr_transfer_attn(epid, -EPROTO); - } - - } else { - - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) { - if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) { - etrax_usb_do_intr_recover(epid); - } else { - panic("Epid attention for epid %d (none INTR), with no errors and no " - "exessive retry r_usb_status is 0x%02X\n", - epid, reg->r_usb_status); - } - - } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) { - panic("Epid attention for epid %d, with no errors and no " - "exessive retry r_usb_status is 0x%02X\n", - epid, reg->r_usb_status); - - } - - warn("Epid attention for epid %d, with no errors and no " - "exessive retry r_usb_status is 0x%02X", - epid, reg->r_usb_status); - warn("OUT error count: %d", IO_EXTRACT(R_USB_EPT_DATA, error_count_out, - r_usb_ept_data)); - warn("IN error count: %d", IO_EXTRACT(R_USB_EPT_DATA, error_count_in, - r_usb_ept_data)); - + /* Read critical registers into local variables, do kmalloc afterwards. */ + save_flags(flags); + cli(); + + irq_mask = *R_USB_IRQ_MASK_READ; + /* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS + must be read before R_USB_EPID_ATTN since reading the latter clears the + ourun and perror fields of R_USB_STATUS. */ + status = *R_USB_STATUS; + + /* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */ + epid_attn = *R_USB_EPID_ATTN; + + /* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the + port_status interrupt. */ + port_status_1 = *R_USB_RH_PORT_STATUS_1; + port_status_2 = *R_USB_RH_PORT_STATUS_2; + + /* Reading R_USB_FM_NUMBER clears the sof interrupt. */ + /* Note: the lower 11 bits contain the actual frame number, sent with each sof. */ + fm_number = *R_USB_FM_NUMBER; - } - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) { - warn("Stall for epid %d", epid); - if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) { - handle_control_transfer_attn(epid, -EPIPE); - } else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) { - handle_bulk_transfer_attn(epid, -EPIPE); - } else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) { - handle_intr_transfer_attn(epid, -EPIPE); - } - - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) { - panic("USB bus error for epid %d\n", epid); - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) { - warn("Buffer error for epid %d", epid); + restore_flags(flags); - if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) { - handle_control_transfer_attn(epid, -EPROTO); - } else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) { - handle_bulk_transfer_attn(epid, -EPROTO); - } else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) { - handle_intr_transfer_attn(epid, -EPROTO); - } + reg = (usb_interrupt_registers_t *)kmem_cache_alloc(top_half_reg_cache, SLAB_ATOMIC); - } - } else if (test_bit(epid, (void *)&ep_really_active)) { - /* Should really be else if (testbit(really active)) */ + assert(reg != NULL); - if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) { + reg->hc = (etrax_hc_t *)vhc; - if (!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable))) { - /* Now we have to verify that this CTRL endpoint got disabled - cause it reached end of list with no error */ - - if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) == - IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { - /* - This means that the endpoint has no error, is disabled - and had inserted traffic, - i.e. transfer sucessfully completed - */ - dbg_ctrl("Last SB for CTRL %d sent sucessfully", epid); - handle_control_transfer_attn(epid, 0); - } - } - - } else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) { - if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable))) { - /* Now we have to verify that this BULK endpoint go disabled - cause it reached end of list with no error */ - - if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) == - IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { - /* - This means that the endpoint has no error, is disabled - and had inserted traffic, - i.e. transfer sucessfully completed - */ - dbg_bulk("Last SB for BULK %d sent sucessfully", epid); - handle_bulk_transfer_attn(epid, 0); - } - } - } else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) { - handle_intr_transfer_attn(epid, 0); - } - } - - } + /* Now put register values into kmalloc'd area. */ + reg->r_usb_irq_mask_read = irq_mask; + reg->r_usb_status = status; + reg->r_usb_epid_attn = epid_attn; + reg->r_usb_rh_port_status_1 = port_status_1; + reg->r_usb_rh_port_status_2 = port_status_2; + reg->r_usb_fm_number = fm_number; + + reg->usb_bh.sync = 0; + reg->usb_bh.routine = etrax_usb_hc_interrupt_bottom_half; + reg->usb_bh.data = reg; + + queue_task(®->usb_bh, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + DBFEXIT; +} + +static void etrax_usb_hc_interrupt_bottom_half(void *data) +{ + usb_interrupt_registers_t *reg = (usb_interrupt_registers_t *)data; + __u32 irq_mask = reg->r_usb_irq_mask_read; + + DBFENTER; - kfree(reg); + /* Interrupts are handled in order of priority. */ + if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) { + etrax_usb_hc_epid_attn_interrupt(reg); + } + if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) { + etrax_usb_hc_port_status_interrupt(reg); + } + if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) { + etrax_usb_hc_ctl_status_interrupt(reg); + } + if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) { + etrax_usb_hc_isoc_eof_interrupt(); + } + if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) { + /* Update/restart the bulk start timer since obviously the channel is running. */ + mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL); + /* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */ + mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL); + + etrax_usb_hc_bulk_eot_interrupt(0); + } + + kmem_cache_free(top_half_reg_cache, reg); DBFEXIT; } -static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs) +void etrax_usb_hc_isoc_eof_interrupt(void) { - struct usb_reg_context *reg; + urb_t *urb; + etrax_urb_priv_t *urb_priv; + int epid; DBFENTER; - reg = (struct usb_reg_context *)kmalloc(sizeof(struct usb_reg_context), GFP_ATOMIC); + /* Do not check the invalid epid (it has a valid sub pointer). */ + for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { + + if (TxIsocEPList[epid].sub == 0) { + /* Nothing here to see. */ + continue; + } - if (!(reg)) { - panic("kmalloc failed in top_half\n"); - } + /* Get the first urb (if any). */ + urb = urb_list_first(epid); + if (urb == 0) { + warn("Ignoring NULL urb"); + continue; + } + + /* Sanity check. */ + assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS); + + urb_priv = (etrax_urb_priv_t *)urb->hcpriv; + assert(urb_priv); + + if (urb_priv->urb_state == NOT_STARTED) { + + /* If ASAP is not set and urb->start_frame is the current frame, + start the transfer. */ + if (!(urb->transfer_flags & USB_ISO_ASAP) && + (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) { + + dbg_isoc("Enabling isoc IN EP descr for epid %d", epid); + TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); + + /* This urb is now active. */ + urb_priv->urb_state = STARTED; + continue; + } + } + } + + DBFEXIT; - reg->hc = (etrax_hc_t *)vhc; - reg->r_usb_irq_mask_read = *R_USB_IRQ_MASK_READ; - reg->r_usb_status = *R_USB_STATUS; +} -#if 0 - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) { - panic("r_usb_status said perror\n"); - } - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) { - panic("r_usb_status said ourun !!!\n"); - } -#endif - - reg->r_usb_epid_attn = *R_USB_EPID_ATTN; +void etrax_usb_hc_bulk_eot_interrupt(int timer_induced) +{ + int epid; + + /* The technique is to run one urb at a time, wait for the eot interrupt at which + point the EP descriptor has been disabled. */ + + DBFENTER; + dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : ""); + + for (epid = 0; epid < NBR_OF_EPIDS; epid++) { + + if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) && + (TxBulkEPList[epid].sub != 0)) { + + urb_t *urb; + etrax_urb_priv_t *urb_priv; + unsigned long flags; + __u32 r_usb_ept_data; + + /* Found a disabled EP descriptor which has a non-null sub pointer. + Verify that this ctrl EP descriptor got disabled no errors. + FIXME: Necessary to check error_code? */ + dbg_bulk("for epid %d?", epid); + + /* Get the first urb. */ + urb = urb_list_first(epid); + + /* FIXME: Could this happen for valid reasons? Why did it disappear? Because of + wrong unlinking? */ + if (!urb) { + warn("NULL urb for epid %d", epid); + continue; + } + + assert(urb); + urb_priv = (etrax_urb_priv_t *)urb->hcpriv; + assert(urb_priv); + + /* Sanity checks. */ + assert(usb_pipetype(urb->pipe) == PIPE_BULK); + if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) { + err("bulk endpoint got disabled before reaching last sb"); + } + + /* For bulk IN traffic, there seems to be a race condition between + between the bulk eot and eop interrupts, or rather an uncertainty regarding + the order in which they happen. Normally we expect the eop interrupt from + DMA channel 9 to happen before the eot interrupt. + + Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */ + + if (usb_pipein(urb->pipe)) { + dbg_bulk("in urb, continuing"); + continue; + } + + save_flags(flags); + cli(); + *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); + nop(); + r_usb_ept_data = *R_USB_EPT_DATA; + restore_flags(flags); + + if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) == + IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { + /* This means that the endpoint has no error, is disabled + and had inserted traffic, i.e. transfer successfully completed. */ + etrax_usb_complete_bulk_urb(urb, 0); + } else { + /* Shouldn't happen. We expect errors to be caught by epid attention. */ + err("Found disabled bulk EP desc, error_code != no_error"); + } + } + } + + /* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer. + However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may + not. Also, we might find two disabled EPs when handling an eot interrupt, and then find + none the next time. */ + + DBFEXIT; + +} + +void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg) +{ + /* This function handles the epid attention interrupt. There are a variety of reasons + for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details): + + invalid ep_id - Invalid epid in an EP (EP disabled). + stall - Not strictly an error condition (EP disabled). + 3rd error - Three successive transaction errors (EP disabled). + buffer ourun - Buffer overrun or underrun (EP disabled). + past eof1 - Intr or isoc transaction proceeds past EOF1. + near eof - Intr or isoc transaction would not fit inside the frame. + zout transfer - If zout transfer for a bulk endpoint (EP disabled). + setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */ + + int epid; + + + DBFENTER; + + /* Note that we loop through all epids. We still want to catch errors for + the invalid one, even though we might handle them differently. */ + for (epid = 0; epid < NBR_OF_EPIDS; epid++) { - reg->r_usb_rh_port_status_1 = *R_USB_RH_PORT_STATUS_1; - reg->r_usb_rh_port_status_2 = *R_USB_RH_PORT_STATUS_2; + if (test_bit(epid, (void *)®->r_usb_epid_attn)) { + + urb_t *urb; + __u32 r_usb_ept_data; + unsigned long flags; + int error_code; + + save_flags(flags); + cli(); + *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); + nop(); + /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO + registers, they are located at the same address and are of the same size. + In other words, this read should be ok for isoc also. */ + r_usb_ept_data = *R_USB_EPT_DATA; + restore_flags(flags); + + /* First some sanity checks. */ + if (epid == INVALID_EPID) { + /* FIXME: What if it became disabled? Could seriously hurt interrupt + traffic. (Use do_intr_recover.) */ + warn("Got epid_attn for INVALID_EPID (%d).", epid); + err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data); + err("R_USB_STATUS = 0x%x", reg->r_usb_status); + continue; + } else if (epid == DUMMY_EPID) { + /* We definitely don't care about these ones. Besides, they are + always disabled, so any possible disabling caused by the + epid attention interrupt is irrelevant. */ + warn("Got epid_attn for DUMMY_EPID (%d).", epid); + continue; + } + + /* Get the first urb in the urb list for this epid. We blatantly assume + that only the first urb could have caused the epid attention. + (For bulk and ctrl, only one urb is active at any one time. For intr + and isoc we remove them once they are completed.) */ + urb = urb_list_first(epid); + + if (urb == NULL) { + err("Got epid_attn for epid %i with no urb.", epid); + err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data); + err("R_USB_STATUS = 0x%x", reg->r_usb_status); + continue; + } + + switch (usb_pipetype(urb->pipe)) { + case PIPE_BULK: + warn("Got epid attn for bulk endpoint, epid %d", epid); + break; + case PIPE_CONTROL: + warn("Got epid attn for control endpoint, epid %d", epid); + break; + case PIPE_INTERRUPT: + warn("Got epid attn for interrupt endpoint, epid %d", epid); + break; + case PIPE_ISOCHRONOUS: + warn("Got epid attn for isochronous endpoint, epid %d", epid); + break; + } + + if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) { + if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) { + warn("Hold was set for epid %d.", epid); + continue; + } + } + + /* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and + R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data); + } else { + error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data); + } + + /* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */ + if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { - reg->usb_bh.sync = 0; - reg->usb_bh.routine = etrax_usb_hc_intr_bottom_half; - reg->usb_bh.data = reg; + /* Isoc traffic doesn't have error_count_in/error_count_out. */ + if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) && + (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 || + IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) { + /* 3rd error. */ + warn("3rd error for epid %i", epid); + etrax_usb_complete_urb(urb, -EPROTO); + + } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) { + + warn("Perror for epid %d", epid); + + if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) { + /* invalid ep_id */ + panic("Perror because of invalid epid." + " Deconfigured too early?"); + } else { + /* past eof1, near eof, zout transfer, setup transfer */ + + /* Dump the urb and the relevant EP descriptor list. */ + + __dump_urb(urb); + __dump_ept_data(epid); + __dump_ep_list(usb_pipetype(urb->pipe)); + + panic("Something wrong with DMA descriptor contents." + " Too much traffic inserted?"); + } + } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) { + /* buffer ourun */ + panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid); + } - queue_task(®->usb_bh, &tq_immediate); - mark_bh(IMMEDIATE_BH); + } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) { + /* Not really a protocol error, just says that the endpoint gave + a stall response. Note that error_code cannot be stall for isoc. */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + panic("Isoc traffic cannot stall"); + } + + warn("Stall for epid %d", epid); + etrax_usb_complete_urb(urb, -EPIPE); + + } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) { + /* Two devices responded to a transaction request. Must be resolved + by software. FIXME: Reset ports? */ + panic("Bus error for epid %d." + " Two devices responded to transaction request", + epid); + + } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) { + /* DMA overrun or underrun. */ + warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid); - DBFEXIT; + /* It seems that error_code = buffer_error in + R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS + are the same error. */ + etrax_usb_complete_urb(urb, -EPROTO); + } + } + } + + DBFEXIT; + +} + +void etrax_usb_bulk_start_timer_func(unsigned long dummy) +{ + + /* We might enable an EP descriptor behind the current DMA position when it's about + to decide that there are no more bulk traffic and it should stop the bulk channel. + Therefore we periodically check if the bulk channel is stopped and there is an + enabled bulk EP descriptor, in which case we start the bulk channel. */ + dbg_bulk("bulk_start_timer timed out."); + + if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) { + int epid; + + dbg_bulk("Bulk DMA channel not running."); + + for (epid = 0; epid < NBR_OF_EPIDS; epid++) { + if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) { + printk("Found enabled EP for epid %d, starting bulk channel.\n", + epid); + *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); + + /* Restart the bulk eot timer since we just started the bulk channel. */ + mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL); + + /* No need to search any further. */ + break; + } + } + } else { + dbg_bulk("Bulk DMA channel running."); + } +} + +void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg) +{ + etrax_hc_t *hc = reg->hc; + __u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1; + __u16 r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2; + + DBFENTER; + + /* The Etrax RH does not include a wPortChange register, so this has to be handled in software + (by saving the old port status value for comparison when the port status interrupt happens). + See section 11.16.2.6.2 in the USB 1.1 spec for details. */ + + dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1); + dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2); + dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1); + dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2); + + /* C_PORT_CONNECTION is set on any transition. */ + hc->rh.wPortChange_1 |= + ((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) != + (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ? + (1 << RH_PORT_CONNECTION) : 0; + + hc->rh.wPortChange_2 |= + ((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) != + (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ? + (1 << RH_PORT_CONNECTION) : 0; + + /* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when + the port is disabled, not when it's enabled. */ + hc->rh.wPortChange_1 |= + ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE)) + && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ? + (1 << RH_PORT_ENABLE) : 0; + + hc->rh.wPortChange_2 |= + ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE)) + && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ? + (1 << RH_PORT_ENABLE) : 0; + + /* C_PORT_SUSPEND is set to one when the device has transitioned out + of the suspended state, i.e. when suspend goes from one to zero. */ + hc->rh.wPortChange_1 |= + ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND)) + && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ? + (1 << RH_PORT_SUSPEND) : 0; + + hc->rh.wPortChange_2 |= + ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND)) + && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ? + (1 << RH_PORT_SUSPEND) : 0; + + + /* C_PORT_RESET is set when reset processing on this port is complete. */ + hc->rh.wPortChange_1 |= + ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET)) + && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ? + (1 << RH_PORT_RESET) : 0; + + hc->rh.wPortChange_2 |= + ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET)) + && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ? + (1 << RH_PORT_RESET) : 0; + + /* Save the new values for next port status change. */ + hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1; + hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2; + + dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1); + dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2); + + DBFEXIT; + +} + +void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg) +{ + DBFENTER; + + /* FIXME: What should we do if we get ourun or perror? Dump the EP and SB + list for the corresponding epid? */ + if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) { + panic("USB controller got ourun."); + } + if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) { + + /* Before, etrax_usb_do_intr_recover was called on this epid if it was + an interrupt pipe. I don't see how re-enabling all EP descriptors + will help if there was a programming error. */ + panic("USB controller got perror."); + } + + if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) { + /* We should never operate in device mode. */ + panic("USB controller in device mode."); + } + + /* These if-statements could probably be nested. */ + if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) { + //info("USB controller in host mode."); + } + if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) { + //info("USB controller started."); + } + if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) { + info("USB controller running."); + } + + DBFEXIT; + } -static int etrax_rh_submit_urb(struct urb *urb) + +static int etrax_rh_submit_urb(urb_t *urb) { struct usb_device *usb_dev = urb->dev; etrax_hc_t *hc = usb_dev->bus->hcpriv; unsigned int pipe = urb->pipe; - struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet; + devrequest *cmd = (devrequest *) urb->setup_packet; void *data = urb->transfer_buffer; int leni = urb->transfer_buffer_length; int len = 0; @@ -1999,10 +3246,14 @@ DBFENTER; + /* FIXME: What is this interrupt urb that is sent to the root hub? */ if (usb_pipetype (pipe) == PIPE_INTERRUPT) { dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval); hc->rh.urb = urb; hc->rh.send = 1; + /* FIXME: We could probably remove this line since it's done + in etrax_rh_init_int_timer. (Don't remove it from + etrax_rh_init_int_timer though.) */ hc->rh.interval = urb->interval; etrax_rh_init_int_timer(urb); DBFEXIT; @@ -2010,15 +3261,22 @@ return 0; } - bmRType_bReq = cmd->bRequestType | cmd->bRequest << 8; - wValue = le16_to_cpu(cmd->wValue); - wIndex = le16_to_cpu(cmd->wIndex); - wLength = le16_to_cpu(cmd->wLength); - - dbg_rh("bmRType_bReq : 0x%04X (%d)", bmRType_bReq, bmRType_bReq); - dbg_rh("wValue : 0x%04X (%d)", wValue, wValue); - dbg_rh("wIndex : 0x%04X (%d)", wIndex, wIndex); - dbg_rh("wLength : 0x%04X (%d)", wLength, wLength); +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 20) + bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8); + wValue = le16_to_cpu(cmd->wValue); + wIndex = le16_to_cpu(cmd->wIndex); + wLength = le16_to_cpu(cmd->wLength); +#else + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = le16_to_cpu(cmd->value); + wIndex = le16_to_cpu(cmd->index); + wLength = le16_to_cpu(cmd->length); +#endif + + dbg_rh("bmRType_bReq : 0x%04x (%d)", bmRType_bReq, bmRType_bReq); + dbg_rh("wValue : 0x%04x (%d)", wValue, wValue); + dbg_rh("wIndex : 0x%04x (%d)", wIndex, wIndex); + dbg_rh("wLength : 0x%04x (%d)", wLength, wLength); switch (bmRType_bReq) { @@ -2050,13 +3308,11 @@ if (wIndex == 1) { *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_1); *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_1); - } - else if (wIndex == 2) { + } else if (wIndex == 2) { *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_2); *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_2); - } - else { - dbg_rh("RH_GET_STATUS whith invalid wIndex !!"); + } else { + dbg_rh("RH_GET_STATUS whith invalid wIndex!"); OK(0); } @@ -2081,9 +3337,10 @@ case (RH_PORT_ENABLE): if (wIndex == 1) { - dbg_rh("trying to do disable of port 1"); + dbg_rh("trying to do disable port 1"); *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes); + while (hc->rh.prev_wPortStatus_1 & IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes)); *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no); @@ -2091,9 +3348,10 @@ } else if (wIndex == 2) { - dbg_rh("trying to do disable of port 2"); + dbg_rh("trying to do disable port 2"); *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes); + while (hc->rh.prev_wPortStatus_2 & IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes)); *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no); @@ -2101,25 +3359,35 @@ } else { dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE " - "with invalid wIndex == %d!!", wIndex); + "with invalid wIndex == %d!", wIndex); } OK (0); case (RH_PORT_SUSPEND): - /* Opposite to suspend should be resume, so well do a resume */ + /* Opposite to suspend should be resume, so we'll do a resume. */ + /* FIXME: USB 1.1, 11.16.2.2 says: + "Clearing the PORT_SUSPEND feature causes a host-initiated resume + on the specified port. If the port is not in the Suspended state, + the hub should treat this request as a functional no-operation." + Shouldn't we check if the port is in a suspended state before + resuming? */ + + /* Make sure the controller isn't busy. */ + while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); + if (wIndex == 1) { *R_USB_COMMAND = IO_STATE(R_USB_COMMAND, port_sel, port1) | - IO_STATE(R_USB_COMMAND, port_cmd, resume)| + IO_STATE(R_USB_COMMAND, port_cmd, resume) | IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); } else if (wIndex == 2) { *R_USB_COMMAND = IO_STATE(R_USB_COMMAND, port_sel, port2) | - IO_STATE(R_USB_COMMAND, port_cmd, resume)| + IO_STATE(R_USB_COMMAND, port_cmd, resume) | IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); } else { dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND " - "with invalid wIndex == %d!!", wIndex); + "with invalid wIndex == %d!", wIndex); } OK (0); @@ -2129,26 +3397,22 @@ if (wIndex == 1) { hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION); - } - else if (wIndex == 2) { + } else if (wIndex == 2) { hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION); - } - else { + } else { dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION " - "with invalid wIndex == %d!!", wIndex); + "with invalid wIndex == %d!", wIndex); } OK (0); case (RH_C_PORT_ENABLE): if (wIndex == 1) { hc->rh.wPortChange_1 &= ~(1 << RH_PORT_ENABLE); - } - else if (wIndex == 2) { + } else if (wIndex == 2) { hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE); - } - else { + } else { dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE " - "with invalid wIndex == %d!!", wIndex); + "with invalid wIndex == %d!", wIndex); } OK (0); case (RH_C_PORT_SUSPEND): @@ -2159,15 +3423,11 @@ case (RH_C_PORT_RESET): if (wIndex == 1) { hc->rh.wPortChange_1 &= ~(1 << RH_PORT_RESET); - } - else if (wIndex == 2) { - dbg_rh("This is wPortChange before clear: 0x%04X", hc->rh.wPortChange_2); - + } else if (wIndex == 2) { hc->rh.wPortChange_2 &= ~(1 << RH_PORT_RESET); - dbg_rh("This is wPortChange after clear: 0x%04X", hc->rh.wPortChange_2); } else { dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_RESET " - "with invalid index == %d!!", wIndex); + "with invalid index == %d!", wIndex); } OK (0); @@ -2178,6 +3438,10 @@ case RH_SET_FEATURE | RH_OTHER | RH_CLASS: switch (wValue) { case (RH_PORT_SUSPEND): + + /* Make sure the controller isn't busy. */ + while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); + if (wIndex == 1) { *R_USB_COMMAND = IO_STATE(R_USB_COMMAND, port_sel, port1) | @@ -2189,76 +3453,150 @@ IO_STATE(R_USB_COMMAND, port_cmd, suspend) | IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); } else { - dbg_rh("RH_SET_FEATURE->RH_C_PORT_SUSPEND " - "with invalid wIndex == %d!!", wIndex); + dbg_rh("RH_SET_FEATURE->RH_PORT_SUSPEND " + "with invalid wIndex == %d!", wIndex); } OK (0); case (RH_PORT_RESET): if (wIndex == 1) { - int port1_retry; - port1_redo: + port_1_reset: dbg_rh("Doing reset of port 1"); + + /* Make sure the controller isn't busy. */ + while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); *R_USB_COMMAND = + IO_STATE(R_USB_COMMAND, port_sel, port1) | IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, port_sel, port1); - - /* We must once again wait at least 10ms for the device to recover */ + IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - port1_retry = 0; - while (!((*((volatile __u16 *)&hc->rh.prev_wPortStatus_1)) & - IO_STATE(R_USB_RH_PORT_STATUS_1, - enabled, yes))) { - printk(""); if (port1_retry++ >= 10000) {goto port1_redo;} - } - - /* This only seems to work if we use printk, - not even schedule() works !!! WHY ?? */ + /* We must wait at least 10 ms for the device to recover. + 15 ms should be enough. */ + udelay(15000); + + /* Wait for reset bit to go low (should be done by now). */ + while (hc->rh.prev_wPortStatus_1 & + IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes)); + + /* If the port status is + 1) connected and enabled then there is a device and everything is fine + 2) neither connected nor enabled then there is no device, also fine + 3) connected and not enabled then we try again + (Yes, there are other port status combinations besides these.) */ + + if ((hc->rh.prev_wPortStatus_1 & + IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) && + (hc->rh.prev_wPortStatus_1 & + IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) { + dbg_rh("Connected device on port 1, but port not enabled?" + " Trying reset again."); + goto port_2_reset; + } + + /* Diagnostic printouts. */ + if ((hc->rh.prev_wPortStatus_1 & + IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) && + (hc->rh.prev_wPortStatus_1 & + IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) { + dbg_rh("No connected device on port 1"); + } else if ((hc->rh.prev_wPortStatus_1 & + IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) && + (hc->rh.prev_wPortStatus_1 & + IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) { + dbg_rh("Connected device on port 1, port 1 enabled"); + } - udelay(15000); - } - else if (wIndex == 2) { - int port2_retry; - - port2_redo: + } else if (wIndex == 2) { + + port_2_reset: dbg_rh("Doing reset of port 2"); + /* Make sure the controller isn't busy. */ + while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); + + /* Issue the reset command. */ *R_USB_COMMAND = + IO_STATE(R_USB_COMMAND, port_sel, port2) | IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, port_sel, port2); - - /* We must once again wait at least 10ms for the device to recover */ + IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - port2_retry = 0; - while (!((*((volatile __u16 *)&hc->rh.prev_wPortStatus_2)) & - IO_STATE(R_USB_RH_PORT_STATUS_2, - enabled, yes))) { - printk(""); if (port2_retry++ >= 10000) {goto port2_redo;} - } - - /* This only seems to work if we use printk, - not even schedule() works !!! WHY ?? */ + /* We must wait at least 10 ms for the device to recover. + 15 ms should be enough. */ + udelay(15000); + + /* Wait for reset bit to go low (should be done by now). */ + while (hc->rh.prev_wPortStatus_2 & + IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes)); + + /* If the port status is + 1) connected and enabled then there is a device and everything is fine + 2) neither connected nor enabled then there is no device, also fine + 3) connected and not enabled then we try again + (Yes, there are other port status combinations besides these.) */ + + if ((hc->rh.prev_wPortStatus_2 & + IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) && + (hc->rh.prev_wPortStatus_2 & + IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) { + dbg_rh("Connected device on port 2, but port not enabled?" + " Trying reset again."); + goto port_2_reset; + } + + /* Diagnostic printouts. */ + if ((hc->rh.prev_wPortStatus_2 & + IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) && + (hc->rh.prev_wPortStatus_2 & + IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) { + dbg_rh("No connected device on port 2"); + } else if ((hc->rh.prev_wPortStatus_2 & + IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) && + (hc->rh.prev_wPortStatus_2 & + IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) { + dbg_rh("Connected device on port 2, port 2 enabled"); + } - udelay(15000); - } + } else { + dbg_rh("RH_SET_FEATURE->RH_PORT_RESET with invalid wIndex = %d", wIndex); + } + + /* Make sure the controller isn't busy. */ + while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); + + /* If all enabled ports were disabled the host controller goes down into + started mode, so we need to bring it back into the running state. + (This is safe even if it's already in the running state.) */ + *R_USB_COMMAND = + IO_STATE(R_USB_COMMAND, port_sel, nop) | + IO_STATE(R_USB_COMMAND, port_cmd, reset) | + IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run); - /* Try to bring the HC into running state */ - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run); - - nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - dbg_rh("...Done"); OK(0); case (RH_PORT_POWER): OK (0); /* port power ** */ case (RH_PORT_ENABLE): - /* There is no rh port enable command in the Etrax USB interface!!!! */ + /* There is no port enable command in the host controller, so if the + port is already enabled, we do nothing. If not, we reset the port + (with an ugly goto). */ + + if (wIndex == 1) { + if (hc->rh.prev_wPortStatus_1 & + IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) { + goto port_1_reset; + } + } else if (wIndex == 2) { + if (hc->rh.prev_wPortStatus_2 & + IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) { + goto port_2_reset; + } + } else { + dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex); + } OK (0); - } break; @@ -2283,8 +3621,10 @@ data, wLength); if (len > 0) { OK(min(leni, len)); - } else + } else { stat = -EPIPE; + } + } break; @@ -2309,33 +3649,56 @@ urb->status = stat; urb->dev = NULL; if (urb->complete) { - urb->complete (urb); + urb->complete(urb); } DBFEXIT; return 0; } +static void +etrax_usb_bulk_eot_timer_func(unsigned long dummy) +{ + /* Because of a race condition in the top half, we might miss a bulk eot. + This timer "simulates" a bulk eot if we don't get one for a while, hopefully + correcting the situation. */ + dbg_bulk("bulk_eot_timer timed out."); + etrax_usb_hc_bulk_eot_interrupt(1); +} + static int __init etrax_usb_hc_init(void) { static etrax_hc_t *hc; struct usb_bus *bus; struct usb_device *usb_rh; - + int i; + DBFENTER; - info("ETRAX 100LX USB-HCD %s (c) 2001 Axis Communications AB\n", usb_hcd_version); - - hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL); + info("ETRAX 100LX USB-HCD %s (c) 2001-2003 Axis Communications AB\n", usb_hcd_version); + + hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL); + assert(hc != NULL); /* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */ - usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0, 0, 0, 0); - if (!usb_desc_cache) { - panic("USB Desc Cache allocation failed !!!\n"); - } - + /* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate + SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) == + sizeof(USB_SB_Desc_t). */ + + usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0, + SLAB_HWCACHE_ALIGN, 0, 0); + assert(usb_desc_cache != NULL); + + top_half_reg_cache = kmem_cache_create("top_half_reg_cache", + sizeof(usb_interrupt_registers_t), + 0, SLAB_HWCACHE_ALIGN, 0, 0); + assert(top_half_reg_cache != NULL); + etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations); hc->bus = bus; +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 20) + bus->bus_name="ETRAX 100LX"; +#endif bus->hcpriv = hc; /* Initalize RH to the default address. @@ -2350,17 +3713,50 @@ hc->rh.prev_wPortStatus_2 = 0; /* Initialize the intr-traffic flags */ + /* FIXME: This isn't used. (Besides, the error field isn't initialized.) */ hc->intr.sleeping = 0; hc->intr.wq = NULL; - /* Initially all ep's are free except ep 0 */ - ep_usage_bitmask = 0; - set_bit(0, (void *)&ep_usage_bitmask); - ep_really_active = 0; - ep_out_traffic = 0; - - memset(URB_List, 0, sizeof(URB_List)); + epid_usage_bitmask = 0; + epid_out_traffic = 0; + /* Mark the invalid epid as being used. */ + set_bit(INVALID_EPID, (void *)&epid_usage_bitmask); + *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID); + nop(); + /* The valid bit should still be set ('invalid' is in our world; not the hardware's). */ + *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) | + IO_FIELD(R_USB_EPT_DATA, max_len, 1)); + + /* Mark the dummy epid as being used. */ + set_bit(DUMMY_EPID, (void *)&epid_usage_bitmask); + *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID); + nop(); + *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) | + IO_FIELD(R_USB_EPT_DATA, max_len, 1)); + + /* Initialize the urb list by initiating a head for each list. */ + for (i = 0; i < NBR_OF_EPIDS; i++) { + INIT_LIST_HEAD(&urb_list[i]); + } + spin_lock_init(&urb_list_lock); + + INIT_LIST_HEAD(&urb_unlink_list); + + + /* Initiate the bulk start timer. */ + init_timer(&bulk_start_timer); + bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL; + bulk_start_timer.function = etrax_usb_bulk_start_timer_func; + add_timer(&bulk_start_timer); + + + /* Initiate the bulk eot timer. */ + init_timer(&bulk_eot_timer); + bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL; + bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func; + add_timer(&bulk_eot_timer); + /* This code should really be moved */ if (request_dma(USB_TX_DMA_NBR, "ETRAX 100LX built-in USB (Tx)")) { @@ -2375,48 +3771,41 @@ etrax_usb_hc_cleanup(); DBFEXIT; return -1; - } -#if 0 /* Moved to head.S */ - *R_GEN_CONFIG = genconfig_shadow = - (genconfig_shadow & ~(IO_MASK(R_GEN_CONFIG, usb1) | - IO_MASK(R_GEN_CONFIG, usb2) | - IO_MASK(R_GEN_CONFIG, dma8) | - IO_MASK(R_GEN_CONFIG, dma9))) | - IO_STATE(R_GEN_CONFIG, dma8, usb) | - IO_STATE(R_GEN_CONFIG, dma9, usb) -#ifdef CONFIG_ETRAX_USB_HOST_PORT1 - | IO_STATE(R_GEN_CONFIG, usb1, select) -#endif -#ifdef CONFIG_ETRAX_USB_HOST_PORT2 - | IO_STATE(R_GEN_CONFIG, usb2, select) -#endif - ; -#endif + } + + /* Set up the data structures for USB traffic. Note that this must be done before + any interrupt that relies on sane DMA list occurrs. */ + init_rx_buffers(); + init_tx_bulk_ep(); + init_tx_ctrl_ep(); + init_tx_intr_ep(); + init_tx_isoc_ep(); + usb_register_bus(hc->bus); - /* We may have to set more bits, but these are the obvious ones */ + /* Note that these interrupts are not used. */ *R_IRQ_MASK2_SET = IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) | IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) | IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) | IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set); + /* Note that the dma9_descr interrupt is not used. */ *R_IRQ_MASK2_SET = IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) | IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set); + /* FIXME: Enable iso_eof only when isoc traffic is running. */ *R_USB_IRQ_MASK_SET = - IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set) | - IO_STATE(R_USB_IRQ_MASK_SET, ctl_eot, set) | + IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) | IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) | -#ifdef ETRAX_USB_INTR_IRQ - IO_STATE(R_USB_IRQ_MASK_SET, intr_eot, set) | -#endif IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) | - IO_STATE(R_USB_IRQ_MASK_SET, port_status, set); + IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) | + IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set); + - if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_intr_top_half, 0, + if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_interrupt_top_half, 0, "ETRAX 100LX built-in USB (HC)", hc)) { err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ); etrax_usb_hc_cleanup(); @@ -2440,16 +3829,50 @@ return -1; } - /* Reset the USB interface (configures as HC) */ + /* R_USB_COMMAND: + USB commands in host mode. The fields in this register should all be + written to in one write. Do not read-modify-write one field at a time. A + write to this register will trigger events in the USB controller and an + incomplete command may lead to unpredictable results, and in worst case + even to a deadlock in the controller. + (Note however that the busy field is read-only, so no need to write to it.) */ + + /* Check the busy bit before writing to R_USB_COMMAND. */ + + while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); + + /* Reset the USB interface. */ *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, ctrl_cmd, reset) | - IO_STATE(R_USB_COMMAND, port_cmd, reset); - - nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); -#if 1 - /* Initate PSTART to all unallocatable bit times */ - *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 10000); -#endif + IO_STATE(R_USB_COMMAND, port_sel, nop) | + IO_STATE(R_USB_COMMAND, port_cmd, reset) | + IO_STATE(R_USB_COMMAND, ctrl_cmd, reset); + + /* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to 0x2A30 (10800), + to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may + allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation. + + While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK + behaviour, it doesn't solve this problem. What happens is that a control transfer will not + be interrupted in its data stage when PSTART happens (the point at which periodic traffic + is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before + PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done, + there may be too little time left for an isochronous transfer, causing an epid attention + interrupt due to perror. The work-around for this is to let the control transfers run at the + end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't + fit into the frame. However, since there will *always* be a control transfer at the beginning + of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer + which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to + this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make + sure that the periodic transfers that are inserted will always fit in the frame. + + The idea was suggested that a control transfer could be split up into several 8 byte transfers, + so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this + hasn't been implemented. + + The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra + for possible bit stuffing. */ + + *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960); #ifdef CONFIG_ETRAX_USB_HOST_PORT1 *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no); @@ -2459,32 +3882,28 @@ *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no); #endif - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config) | - IO_STATE(R_USB_COMMAND, port_cmd, reset); - - nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); + while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); + /* Configure the USB interface as a host controller. */ *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port1) | - IO_STATE(R_USB_COMMAND, port_cmd, reset); - - nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); + IO_STATE(R_USB_COMMAND, port_sel, nop) | + IO_STATE(R_USB_COMMAND, port_cmd, reset) | + IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config); + + /* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled + sequence of resetting the ports. If we reset both ports now, and there are devices + on both ports, we will get a bus error because both devices will answer the set address + request. */ - /* Here we must wait at least 10ms so the device has time to recover */ - udelay(15000); + while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - init_rx_buffers(); - init_tx_bulk_ep(); - init_tx_ctrl_ep(); - init_tx_intr_ep(); - - /* This works. It seems like the host_run command only has effect when a device is connected, - i.e. it has to be done when a interrup */ + /* Start processing of USB traffic. */ *R_USB_COMMAND = + IO_STATE(R_USB_COMMAND, port_sel, nop) | + IO_STATE(R_USB_COMMAND, port_cmd, reset) | IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run); - - nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); + + while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); usb_rh = usb_alloc_dev(NULL, hc->bus); hc->bus->root_hub = usb_rh; @@ -2508,6 +3927,8 @@ free_dma(USB_RX_DMA_NBR); usb_deregister_bus(etrax_usb_bus); + /* FIXME: call kmem_cache_destroy here? */ + DBFEXIT; } diff -urN linux-2.4.21-bk5/arch/cris/drivers/usb-host.h linux-2.4.21-bk6/arch/cris/drivers/usb-host.h --- linux-2.4.21-bk5/arch/cris/drivers/usb-host.h 2001-08-15 01:22:15.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/drivers/usb-host.h 2003-07-10 02:54:58.000000000 -0700 @@ -5,27 +5,27 @@ #include typedef struct USB_IN_Desc { - __u16 sw_len; - __u16 command; - unsigned long next; - unsigned long buf; - __u16 hw_len; - __u16 status; + volatile __u16 sw_len; + volatile __u16 command; + volatile unsigned long next; + volatile unsigned long buf; + volatile __u16 hw_len; + volatile __u16 status; } USB_IN_Desc_t; typedef struct USB_SB_Desc { - __u16 sw_len; - __u16 command; - unsigned long next; - unsigned long buf; + volatile __u16 sw_len; + volatile __u16 command; + volatile unsigned long next; + volatile unsigned long buf; __u32 dummy; } USB_SB_Desc_t; typedef struct USB_EP_Desc { - __u16 hw_len; - __u16 command; - unsigned long sub; - unsigned long nep; + volatile __u16 hw_len; + volatile __u16 command; + volatile unsigned long sub; + volatile unsigned long next; __u32 dummy; } USB_EP_Desc_t; @@ -37,10 +37,10 @@ int interval; int numports; struct timer_list rh_int_timer; - __u16 wPortChange_1; - __u16 wPortChange_2; - __u16 prev_wPortStatus_1; - __u16 prev_wPortStatus_2; + volatile __u16 wPortChange_1; + volatile __u16 wPortChange_2; + volatile __u16 prev_wPortStatus_1; + volatile __u16 prev_wPortStatus_2; }; struct etrax_usb_intr_traffic { @@ -55,40 +55,77 @@ struct etrax_usb_intr_traffic intr; } etrax_hc_t; -typedef enum {idle, eot, nodata} etrax_usb_rx_state_t; +typedef enum { + STARTED, + NOT_STARTED, + UNLINK +} etrax_usb_urb_state_t; + + typedef struct etrax_usb_urb_priv { + /* The first_sb field is used for freeing all SB descriptors belonging + to an urb. The corresponding ep descriptor's sub pointer cannot be + used for this since the DMA advances the sub pointer as it processes + the sb list. */ USB_SB_Desc_t *first_sb; + /* The last_sb field referes to the last SB descriptor that belongs to + this urb. This is important to know so we can free the SB descriptors + that ranges between first_sb and last_sb. */ + USB_SB_Desc_t *last_sb; + + /* The rx_offset field is used in ctrl and bulk traffic to keep track + of the offset in the urb's transfer_buffer where incoming data should be + copied to. */ __u32 rx_offset; - etrax_usb_rx_state_t rx_state; - __u8 eot; - struct list_head ep_in_list; -} etrax_urb_priv_t; + /* Counter used in isochronous transfers to keep track of the + number of packets received/transmitted. */ + __u32 isoc_packet_counter; + + /* This field is used to pass information about the urb's current state between + the various interrupt handlers (thus marked volatile). */ + volatile etrax_usb_urb_state_t urb_state; + + /* Connection between the submitted urb and ETRAX epid number */ + __u8 epid; + + /* The rx_data_list field is used for periodic traffic, to hold + received data for later processing in the the complete_urb functions, + where the data us copied to the urb's transfer_buffer. Basically, we + use this intermediate storage because we don't know when it's safe to + reuse the transfer_buffer (FIXME?). */ + struct list_head rx_data_list; +} etrax_urb_priv_t; -struct usb_reg_context +/* This struct is for passing data from the top half to the bottom half. */ +typedef struct usb_interrupt_registers { etrax_hc_t *hc; __u32 r_usb_epid_attn; __u8 r_usb_status; - __u32 r_usb_rh_port_status_1; - __u32 r_usb_rh_port_status_2; + __u16 r_usb_rh_port_status_1; + __u16 r_usb_rh_port_status_2; __u32 r_usb_irq_mask_read; + __u32 r_usb_fm_number; struct tq_struct usb_bh; -#if 0 - __u32 r_usb_ept_data[32]; -#endif -}; +} usb_interrupt_registers_t; -struct in_chunk +/* This struct holds data we get from the rx descriptors for DMA channel 9 + for periodic traffic (intr and isoc). */ +typedef struct rx_data { void *data; int length; - char epid; struct list_head list; -}; +} rx_data_t; + +typedef struct urb_entry +{ + urb_t *urb; + struct list_head list; +} urb_entry_t; - /* --------------------------------------------------------------------------- Virtual Root HUB ------------------------------------------------------------------------- */ diff -urN linux-2.4.21-bk5/arch/cris/drivers/virtex.c linux-2.4.21-bk6/arch/cris/drivers/virtex.c --- linux-2.4.21-bk5/arch/cris/drivers/virtex.c 2002-11-28 15:53:09.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/drivers/virtex.c 2003-07-10 02:54:58.000000000 -0700 @@ -7,7 +7,7 @@ *! *! The FPGA can be programmed by copying the bit-file to /dev/fpga. *! -*! cp fpga.bit > /dev/fpga +*! cat fpga.bit > /dev/fpga *! *! Kernel log should look like: *! 69900 bytes written @@ -31,6 +31,9 @@ *! *! Jul 19 2002 Stefan Lundberg Initial version. *! $Log: virtex.c,v $ +*! Revision 1.2 2003/02/24 07:50:30 fredriko +*! Bugfixes and cleanups. +*! *! Revision 1.1 2002/06/25 09:58:58 stefanl *! New FPGA driver for Platoon *! @@ -40,7 +43,7 @@ *! (C) Copyright 2002 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: virtex.c,v 1.1 2002/06/25 09:58:58 stefanl Exp $ */ +/* $Id: virtex.c,v 1.2 2003/02/24 07:50:30 fredriko Exp $ */ /****************** INCLUDE FILES SECTION ***********************************/ #include @@ -127,14 +130,20 @@ void start_virtex_program(void) { + volatile unsigned int i=0; unsigned short reg_data=0; printk("Start writing to FPGA\n"); - reg_data = SET_CS_BIT(reg_data); // FPGA unselected + reg_data = SET_CS_BIT(reg_data); // FPGA unselected, PROGRAM bit not set + WRITE_FPGA_PROG_REG(reg_data); + for(i=0;i<10;i++) { } // at least 300 ns loop + reg_data = SET_PROGRAM_BIT(reg_data); WRITE_FPGA_PROG_REG(reg_data); + for(i=0;i<10;i++) { } // at least 300 ns loop + while(!READ_INIT); // Wait for init reg_data = SET_WRITE_BIT(reg_data); @@ -304,6 +313,7 @@ } if(copy_from_user(ptr, buf, count)) { printk("copy_from_user failed\n"); + kfree(ptr); return -EFAULT; } @@ -339,7 +349,7 @@ switch (_IOC_NR(cmd)) { case VIRTEX_FPGA_WRITEREG: /* write to an FPGA register */ - VIRTEX_DEBUG(printk("virtex wr %d %d\n", + VIRTEX_DEBUG(printk("virtex wr 0x%x = 0x%x\n", VIRTEX_FPGA_ARGREG(arg), VIRTEX_FPGA_ARGVALUE(arg))); @@ -347,12 +357,12 @@ VIRTEX_FPGA_ARGVALUE(arg)); case VIRTEX_FPGA_READREG: { - unsigned char val; + unsigned short val; /* read from an FPGA register */ - VIRTEX_DEBUG(printk("virtex rd %d ", + VIRTEX_DEBUG(printk("virtex rd 0x%x ", VIRTEX_FPGA_ARGREG(arg))); val = virtex_readreg(VIRTEX_FPGA_ARGREG(arg)); - VIRTEX_DEBUG(printk("= %d\n", val)); + VIRTEX_DEBUG(printk("= 0x%x\n", val)); return val; } default: diff -urN linux-2.4.21-bk5/arch/cris/drivers/virtex.h linux-2.4.21-bk6/arch/cris/drivers/virtex.h --- linux-2.4.21-bk5/arch/cris/drivers/virtex.h 2002-11-28 15:53:09.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/drivers/virtex.h 2003-07-10 02:54:58.000000000 -0700 @@ -1,7 +1,2 @@ -/* $Id: virtex.h,v 1.1 2002/06/25 09:58:58 stefanl Exp $ */ - int virtex_writereg(unsigned short theReg, unsigned short theValue); unsigned short virtex_readreg(unsigned short theReg); - - - diff -urN linux-2.4.21-bk5/arch/cris/kernel/Makefile linux-2.4.21-bk6/arch/cris/kernel/Makefile --- linux-2.4.21-bk5/arch/cris/kernel/Makefile 2002-11-28 15:53:09.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/kernel/Makefile 2003-07-10 02:54:58.000000000 -0700 @@ -1,4 +1,3 @@ -# $Id: Makefile,v 1.10 2002/05/27 15:41:40 johana Exp $ # # Makefile for the linux kernel. # @@ -17,21 +16,24 @@ O_TARGET := kernel.o -export-objs := ksyms.o +export-objs := crisksyms.o obj-y := process.o signal.o entry.o traps.o irq.o \ ptrace.o setup.o time.o sys_cris.o shadows.o \ debugport.o semaphore.o -obj-$(CONFIG_MODULES) += ksyms.o +obj-$(CONFIG_MODULES) += crisksyms.o obj-$(CONFIG_ETRAX_KGDB) += kgdb.o obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o +obj-$(CONFIG_ETRAX_DEBUG_INTERRUPT) += debug.o # This dependency isn't caught by mkdep. See entry.S. entry.o: entryoffsets.s +# We don't want debug info for the dummy entryoffsets functions, and the +# assembler gets confused when file directives are .if 0:d out. entryoffsets.s: entryoffsets.c - $(CC) $(CFLAGS) -S -c $< + $(CC) $(subst -g,,$(CFLAGS)) -S -c $< clean: diff -urN linux-2.4.21-bk5/arch/cris/kernel/crisksyms.c linux-2.4.21-bk6/arch/cris/kernel/crisksyms.c --- linux-2.4.21-bk5/arch/cris/kernel/crisksyms.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/kernel/crisksyms.c 2003-07-10 02:54:58.000000000 -0700 @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void dump_thread(struct pt_regs *, struct user *); +extern unsigned long get_cmos_time(void); +extern void __Div(void); +extern void __Mod(void); +extern void __Udiv(void); +extern void __Umod(void); +extern void __ashrdi3(void); +extern void iounmap(void *addr); + +/* Platform dependent support */ +EXPORT_SYMBOL(dump_thread); +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(get_cmos_time); +EXPORT_SYMBOL(loops_per_usec); + +/* String functions */ +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(strtok); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strstr); +EXPORT_SYMBOL(strcpy); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strncmp); +EXPORT_SYMBOL(strncpy); + +/* Math functions */ +EXPORT_SYMBOL_NOVERS(__Div); +EXPORT_SYMBOL_NOVERS(__Mod); +EXPORT_SYMBOL_NOVERS(__Udiv); +EXPORT_SYMBOL_NOVERS(__Umod); +EXPORT_SYMBOL_NOVERS(__ashrdi3); + +/* Memory functions */ +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); + +/* Semaphore functions */ +EXPORT_SYMBOL(__up); +EXPORT_SYMBOL(__down); +EXPORT_SYMBOL(__down_interruptible); +EXPORT_SYMBOL(__down_trylock); + +/* 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); + +/* Userspace access functions */ +EXPORT_SYMBOL(__copy_user_zeroing); +EXPORT_SYMBOL(__copy_user); + +/* Cache flush functions */ +EXPORT_SYMBOL(flush_etrax_cache); +EXPORT_SYMBOL(prepare_rx_descriptor); + +#undef memcpy +#undef memset +extern void * memset(void *, int, __kernel_size_t); +extern void * memcpy(void *, const void *, __kernel_size_t); +EXPORT_SYMBOL_NOVERS(memcpy); +EXPORT_SYMBOL_NOVERS(memset); + +#ifdef CONFIG_ETRAX_FAST_TIMER +/* Fast timer functions */ +EXPORT_SYMBOL(fast_timer_list); +EXPORT_SYMBOL(start_one_shot_timer); +EXPORT_SYMBOL(del_fast_timer); +EXPORT_SYMBOL(schedule_usleep); +#endif diff -urN linux-2.4.21-bk5/arch/cris/kernel/debug.c linux-2.4.21-bk6/arch/cris/kernel/debug.c --- linux-2.4.21-bk5/arch/cris/kernel/debug.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/kernel/debug.c 2003-07-10 02:54:58.000000000 -0700 @@ -0,0 +1,213 @@ +/* + * arch/cris/kernel/debug.c + * Various debug routines: + * o Logging of interrupt enabling/disabling. /proc/debug_interrupt + * gives result and toggles if it is enabled or not. + * + * Copyright (C) 2003 Axis Communications AB + */ + +#include +#include +#include +#include +#include + +#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT +#define LOG_INT_SIZE 8192 +#define DEBUG_INT_PROC_FILE "debug_interrupt" +#define LOG_INT_SHOW_MIN_USEC 45000 + +/* These are global and can be used to trig certain events. */ +int log_int_pos = 0; +int log_int_size = LOG_INT_SIZE; +int log_int_trig0_pos = 0; +int log_int_trig1_pos = 0; + +int log_int_enable = 0; /* toggled every read of /proc/debug_interrupt */ + +struct log_int_struct +{ + unsigned long pc; + unsigned long ev_timer_data; +}; + +static struct log_int_struct log_ints[LOG_INT_SIZE]; + +//static unsigned long prev_log_int_t = 0; +static unsigned long prev_logged_ccr = 0; +static unsigned long prev_ei_timer_data = 0; +static unsigned long prev_di_timer_data = 0; + +#define CCR_EI_BIT 5 +enum { + INT_OLD_MASK = 0x01, INT_OLD_BIT = 0, + INT_NEW_MASK = 0x02, INT_NEW_BIT = 1, + INT_EI_CHANGE_MASK = 0x04, INT_EI_CHANGE_BIT = 2, + INT_ACTION_RESTORE = 0x08, INT_ACTION_RESTORE_BIT = 3, + INT_ACTION_MISSED = 0x10, INT_ACTION_MISSED_BIT = 4, + INT_ACTION_LONG = 0x20, INT_ACTION_LONG_BIT = 5, + + INT_OLD_EI = INT_OLD_MASK, INT_OLD_EI_BIT = INT_OLD_BIT, + INT_NEW_EI = INT_NEW_MASK, INT_NEW_EI_BIT = INT_NEW_BIT, + INT_EV_DI = INT_OLD_EI | INT_EI_CHANGE_MASK, + INT_EV_NOP_DI = 0, + INT_EV_EI = INT_NEW_EI | INT_EI_CHANGE_MASK, + INT_EV_NOP_EI = INT_OLD_EI | INT_NEW_EI, + INT_EV_RESTORE_DI= INT_EV_DI | INT_EI_CHANGE_MASK | INT_ACTION_RESTORE, + INT_EV_RESTORE_EI= INT_EV_EI | INT_EI_CHANGE_MASK | INT_ACTION_RESTORE, + INT_EV_RESTORE_NOP_DI = INT_EV_NOP_DI | INT_ACTION_RESTORE, + INT_EV_RESTORE_NOP_EI = INT_EV_NOP_EI | INT_ACTION_RESTORE, +}; + +void log_int(unsigned long pc, unsigned long curr_ccr, unsigned long next_ccr) +{ + unsigned long t; + int ev; + static int no_change_cnt = 0; + + /* Just disable interrupts without logging here, + * the caller will either do ei, di or restore + */ + __asm__ __volatile__ ("di" : : :"memory"); + t = *R_TIMER_DATA; + + if (curr_ccr & CCR_EI_MASK) { + prev_ei_timer_data = t; + ev = INT_OLD_EI; + } else { + prev_di_timer_data = t; + if ( (((prev_ei_timer_data >> 8) & 0x000000FF) - + ((prev_di_timer_data >> 8) & 0x000000FF)) & ~0x03) + ev = INT_ACTION_LONG; + else + ev = 0; + } + if ((curr_ccr ^ prev_logged_ccr) & CCR_EI_MASK) + ev |= INT_ACTION_MISSED; + if (next_ccr & CCR_EI_MASK) + ev |= INT_NEW_EI; + if ((curr_ccr ^ next_ccr) & CCR_EI_MASK) { + ev |= INT_EI_CHANGE_MASK; + no_change_cnt = 0; + } else + no_change_cnt++; + + prev_logged_ccr = next_ccr; + + if (log_int_enable && + ((ev & (INT_EI_CHANGE_MASK | INT_ACTION_MISSED | INT_ACTION_LONG)) + || (no_change_cnt < 40)) && + log_int_pos < LOG_INT_SIZE) { + int i; + i = log_int_pos; + log_int_pos++; + log_ints[i].pc = pc; + log_ints[i].ev_timer_data = (t & 0x00FFFFFF) | + ((ev & 0xFF) << 24); + + } +// __asm__ __volatile__ ("move %0,$ccr" : : "rm" (curr_ccr) : "memory"); +} +void log_int_di(void) +{ + unsigned long pc; + unsigned long flags; + __asm__ __volatile__ ("move $srp,%0" : "=rm" (pc) : : "memory"); + __asm__ __volatile__ ("move $ccr,%0" : "=rm" (flags) : : "memory"); + log_int(pc, flags, 0); +} + +void log_int_ei(void) +{ + unsigned long pc; + unsigned long flags; + __asm__ __volatile__ ("move $srp,%0" : "=rm" (pc) : : "memory"); + __asm__ __volatile__ ("move $ccr,%0" : "=rm" (flags) : : "memory"); + log_int(pc, flags, CCR_EI_MASK); +} + + +static int log_int_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0; + off_t begin = 0; + int j, t0, t1, t, thigh, tlow, tdi0; + int di0, ei1; + len += sprintf(page + len, "trig0: %i trig1: %i\n", log_int_trig0_pos, log_int_trig1_pos); + for (i = 0; i < log_int_pos-1; i++) { + + if (((log_ints[i].ev_timer_data >> 24) & INT_NEW_EI)) { + di0 = i; + while (di0 < log_int_pos-1 && (((log_ints[di0].ev_timer_data >> 24) & INT_NEW_EI))) + di0++; + ei1 = di0+1; + while (ei1 < log_int_pos-1 && (((log_ints[ei1].ev_timer_data >> 24) & INT_NEW_EI) == 0)) + ei1++; + thigh = timer_data_to_ns(log_ints[ei1].ev_timer_data); + tlow = timer_data_to_ns(log_ints[di0].ev_timer_data); + tdi0 = timer_data_to_ns(log_ints[di0].ev_timer_data); + + t = thigh-tlow; + j = di0-1; + if ((t > LOG_INT_SHOW_MIN_USEC) || (log_int_trig0_pos-30 < j && j < log_int_trig1_pos+30)) { + + for (; j <= ei1; j++) { + t0 = ((log_ints[j+1].ev_timer_data & 0xFF) - + (log_ints[j].ev_timer_data & 0xFF)); + t1 = (((log_ints[j+1].ev_timer_data >> 8) & 0xFF) - + ((log_ints[j].ev_timer_data >> 8) & 0xFF)); + if (t1 == 0 || t1 == 1) { + if (t0 < 0) + t0 += 256; + } else { + if (t1 < 0) + t1 += 256; + } + thigh = timer_data_to_ns(log_ints[j+1].ev_timer_data); + tlow = timer_data_to_ns(log_ints[j].ev_timer_data); + t = thigh-tlow; + len += sprintf(page + len, "%4i PC %08lX-%08lX %08lX-%08lX %s high %i in %-6i ns %-7i to %-7i = %-5i ns, from first di %i ns %s\n", + j, log_ints[j].pc, log_ints[j+1].pc, + log_ints[j].ev_timer_data, log_ints[j+1].ev_timer_data, + ((log_ints[j].ev_timer_data >> 24) & INT_NEW_EI)!= 0?"ei":"di", t1, t, + tlow, thigh, thigh-tlow, thigh-tdi0, + j==log_int_trig0_pos?"TRIG0":(j==log_int_trig1_pos?"TRIG1":"")); + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + + len += sprintf(page + len,"\n"); + i = ei1-2; + } + + } + + } + log_int_enable = 1; + log_int_pos = 0; + log_int_trig0_pos = 0; + log_int_trig1_pos = 0; + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (off-begin); + return ((count < begin+len-off) ? count : begin+len-off); +} + +static int __init +log_int_init(void) +{ + create_proc_read_entry (DEBUG_INT_PROC_FILE, 0, 0, log_int_read_proc, NULL); + printk(KERN_INFO "/proc/" DEBUG_INT_PROC_FILE " size %i.\r\n", + LOG_INT_SIZE); + return 0; +} +module_init(log_int_init); +#endif /* CONFIG_ETRAX_DEBUG_INTERRUPT */ diff -urN linux-2.4.21-bk5/arch/cris/kernel/debugport.c linux-2.4.21-bk6/arch/cris/kernel/debugport.c --- linux-2.4.21-bk5/arch/cris/kernel/debugport.c 2002-08-02 17:39:42.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/kernel/debugport.c 2003-07-10 02:54:58.000000000 -0700 @@ -12,6 +12,17 @@ * init_etrax_debug() * * $Log: debugport.c,v $ + * Revision 1.9 2003/02/17 07:10:34 starvik + * Last merge was incomplete + * + * Revision 1.8 2003/02/17 06:59:00 starvik + * Merged printk corruption fix + * + * Revision 1.7.4.1 2003/01/27 10:21:57 starvik + * Solved the problem with corrupted debug output + * * Wait until DMA, FIFO and pipe is empty before and after transmissions + * * Buffer data until a FIFO flush can be triggered. + * * Revision 1.7 2002/04/23 15:35:50 bjornw * Cleaned up sercons struct and removed the waitkey ptr (2.4.19-pre) * @@ -31,6 +42,7 @@ #include #include #include +#include #include #include @@ -94,6 +106,8 @@ #define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma4_descr, clr) #endif +#define MIN_SIZE 32 /* Size that triggers the FIFO to flush characters to interface */ + /* Write a string of count length to the console (debug port) using DMA, polled * for completion. Interrupts are disabled during the whole process. Some * caution needs to be taken to not interfere with ttyS business on this port. @@ -103,8 +117,11 @@ console_write(struct console *co, const char *buf, unsigned int len) { static struct etrax_dma_descr descr; + static struct etrax_dma_descr descr2; + static char tmp_buf[MIN_SIZE]; + static int tmp_size = 0; + unsigned long flags; - int in_progress; #ifdef CONFIG_ETRAX_DEBUG_PORT_NULL /* no debug printout at all */ @@ -126,6 +143,37 @@ restore_flags(flags); return; #endif + /* To make this work together with the real serial port driver + * we have to make sure that everything is flushed when we leave + * here. The following steps are made to assure this: + * 1. Wait until DMA stops, FIFO is empty and serial port pipeline empty. + * 2. Write at least half the FIFO to trigger flush to serial port. + * 3. Wait until DMA stops, FIFO is empty and serial port pipeline empty. + */ + + /* Do we have enough characters to make the DMA/FIFO happy? */ + if (tmp_size + len < MIN_SIZE) + { + int size = min((int)(MIN_SIZE - tmp_size),(int)len); + memcpy(&tmp_buf[tmp_size], buf, size); + tmp_size += size; + len -= size; + + /* Pad with space if complete line */ + if (tmp_buf[tmp_size-1] == '\n') + { + memset(&tmp_buf[tmp_size-1], ' ', MIN_SIZE - tmp_size); + tmp_buf[MIN_SIZE - 1] = '\n'; + tmp_size = MIN_SIZE; + len = 0; + } + else + { + /* Wait for more characters */ + restore_flags(flags); + return; + } + } /* make sure the transmitter is enabled. * NOTE: this overrides any setting done in ttySx, to 8N1, no auto-CTS. @@ -134,37 +182,36 @@ */ *DEBUG_TR_CTRL = 0x40; + while(*DEBUG_OCMD & 7); /* Until DMA is not running */ + while(*DEBUG_STATUS & 0x7f); /* wait until output FIFO is empty as well */ + udelay(200); /* Wait for last two characters to leave the serial transmitter */ - /* if the tty has some ongoing business, remember it */ - - in_progress = *DEBUG_OCMD & 7; - - if(in_progress) { - /* wait until the output dma channel is ready */ - - while(*DEBUG_OCMD & 7) /* nothing */ ; + if (tmp_size) + { + descr.ctrl = len ? 0 : d_eop | d_wait | d_eol; + descr.sw_len = tmp_size; + descr.buf = virt_to_phys(tmp_buf); + descr.next = virt_to_phys(&descr2); + descr2.ctrl = d_eop | d_wait | d_eol; + descr2.sw_len = len; + descr2.buf = virt_to_phys((char*)buf); + } + else + { + descr.ctrl = d_eop | d_wait | d_eol; + descr.sw_len = len; + descr.buf = virt_to_phys((char*)buf); } - descr.ctrl = d_eol; - descr.sw_len = len; - descr.buf = __pa(buf); - - *DEBUG_FIRST = __pa(&descr); /* write to R_DMAx_FIRST */ + *DEBUG_FIRST = virt_to_phys(&descr); /* write to R_DMAx_FIRST */ *DEBUG_OCMD = 1; /* dma command start -> R_DMAx_CMD */ /* wait until the output dma channel is ready again */ + while(*DEBUG_OCMD & 7); + while(*DEBUG_STATUS & 0x7f); + udelay(200); - while(*DEBUG_OCMD & 7) /* nothing */; - - /* clear pending interrupts so we don't get a surprise below */ - - if(in_progress) - *DEBUG_OCLRINT = 2; /* only clear EOP, leave DESCR for the tty */ - else - *DEBUG_OCLRINT = 3; /* clear both EOP and DESCR */ - - while(*DEBUG_STATUS & 0x7f); /* wait until output FIFO is empty as well */ - + tmp_size = 0; restore_flags(flags); } diff -urN linux-2.4.21-bk5/arch/cris/kernel/entry.S linux-2.4.21-bk6/arch/cris/kernel/entry.S --- linux-2.4.21-bk5/arch/cris/kernel/entry.S 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/kernel/entry.S 2003-07-10 02:54:58.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.40 2002/12/02 08:11:41 starvik Exp $ +/* $Id: entry.S,v 1.42 2003/07/07 09:00:50 johana Exp $ * * linux/arch/cris/entry.S * @@ -7,6 +7,12 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: entry.S,v $ + * Revision 1.42 2003/07/07 09:00:50 johana + * Added CONFIG_ETRAX_DEBUG_INTERRUPT support. + * + * Revision 1.41 2003/06/17 13:57:50 starvik + * Merge of Linux 2.4.21 + * * Revision 1.40 2002/12/02 08:11:41 starvik * Merge of Linux 2.4.20 * @@ -358,6 +364,12 @@ _Rexit: ;; this epilogue MUST match the prologues in multiple_interrupt, irq.h and ptregs.h +#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT + move.d $pc,$r10 + move $ccr,$r11 + move.d [$sp + LDCCR], $r12; regs->dccr + jsr log_int +#endif pop $r10 ; frametype bne _RBFexit ; was not CRIS_FRAME_NORMAL, handle otherwise addq 4, $sp ; skip orig_r10, in delayslot @@ -449,6 +461,22 @@ push $srp ; we keep the old/new PC on the stack add.d $r12, $r10 ; r10 = current tasks tss move $dccr, [$r10+LTHREAD_DCCR] ; save irq enable state +#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT + push $r9 + push $r10 + push $r11 + push $r12 + push $r13 + move.d $pc,$r10 + move $ccr,$r11 + clear.d $r12 + jsr log_int + pop $r13 + pop $r12 + pop $r11 + pop $r10 + pop $r9 +#endif di move $usp, [$r10+LTHREAD_USP] ; save user-mode stackpointer diff -urN linux-2.4.21-bk5/arch/cris/kernel/fasttimer.c linux-2.4.21-bk6/arch/cris/kernel/fasttimer.c --- linux-2.4.21-bk5/arch/cris/kernel/fasttimer.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/kernel/fasttimer.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,10 +1,16 @@ -/* $Id: fasttimer.c,v 1.5 2002/10/15 06:21:39 starvik Exp $ +/* $Id: fasttimer.c,v 1.7 2003/04/01 14:12:07 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.7 2003/04/01 14:12:07 starvik + * Added loglevel for lots of printks + * + * Revision 1.6 2003/02/10 17:05:44 pkj + * Made fast_timer_pending() public. + * * Revision 1.5 2002/10/15 06:21:39 starvik * Added call to init_waitqueue_head * @@ -63,7 +69,7 @@ * Revision 1.1 2000/10/26 15:49:16 johana * Added fasttimer, highresolution timers. * - * Copyright (C) 2000,2001 2002 Axis Communications AB, Lund, Sweden + * Copyright (C) 2000,2001,2002,2003 Axis Communications AB, Lund, Sweden */ #include @@ -118,7 +124,7 @@ static int fast_timer_is_init = 0; static int fast_timer_ints = 0; -static struct fast_timer *fast_timer_list = NULL; +struct fast_timer *fast_timer_list = NULL; #ifdef DEBUG_LOG_INCLUDED #define DEBUG_LOG_MAX 128 @@ -313,7 +319,8 @@ { if (tmp == t) { - printk("timer name: %s data: 0x%08lX already in list!\n", name, data); + printk(KERN_WARNING + "timer name: %s data: 0x%08lX already in list!\n", name, data); sanity_failed++; return; } @@ -379,11 +386,6 @@ 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; @@ -770,7 +772,7 @@ cli(); if (t->next != nextt) { - printk("timer removed!\n"); + printk(KERN_WARNING "timer removed!\n"); } t = nextt; } @@ -951,7 +953,7 @@ int i; #endif - printk("fast_timer_init()\n"); + printk(KERN_INFO "fast_timer_init()\n"); #if 0 && defined(FAST_TIMER_TEST) for (i = 0; i <= TIMER0_DIV; i++) @@ -971,7 +973,7 @@ if(request_irq(TIMER1_IRQ_NBR, timer1_handler, SA_SHIRQ, "fast timer int", NULL)) { - printk("err: timer1 irq\n"); + printk(KERN_CRIT "err: timer1 irq\n"); } fast_timer_is_init = 1; #ifdef FAST_TIMER_TEST diff -urN linux-2.4.21-bk5/arch/cris/kernel/head.S linux-2.4.21-bk6/arch/cris/kernel/head.S --- linux-2.4.21-bk5/arch/cris/kernel/head.S 2002-08-02 17:39:42.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/kernel/head.S 2003-07-10 02:54:58.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.44 2002/02/05 15:39:57 bjornw Exp $ +/* $Id: head.S,v 1.47 2003/07/08 09:53:35 starvik Exp $ * * Head of the kernel - alter with care * @@ -7,6 +7,15 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: head.S,v $ + * Revision 1.47 2003/07/08 09:53:35 starvik + * Corrected last CVS log entry + * + * Revision 1.46 2003/05/15 08:07:51 gunnard + * Removed serial port DMA when using AGDB. + * + * Revision 1.45 2003/04/28 05:31:05 starvik + * Added section attributes + * * Revision 1.44 2002/02/05 15:39:57 bjornw * Oops.. non-CRAMFS_MAGIC should jump over the copying, not into it... * @@ -311,7 +320,7 @@ ;; Put this in a suitable section where we can reclaim storage ;; after init. - .section ".text.init" + .section ".text.init", "ax" _inflash: #ifdef CONFIG_ETRAX_ETHERNET ;; Start MII clock to make sure it is running when tranceiver is reset @@ -543,14 +552,24 @@ #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 +#if (defined(CONFIG_ETRAX_AGDB)) + ; agdb only wants DMA out, no dma in + or.d IO_STATE (R_GEN_CONFIG, dma6, serial0),$r0 +#else or.d IO_STATE (R_GEN_CONFIG, dma7, serial0) \ | IO_STATE (R_GEN_CONFIG, dma6, serial0),$r0 #endif +#endif #if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT1) ; DMA channels 8 and 9 to ser1, kgdb doesnt want DMA +#if (defined(CONFIG_ETRAX_AGDB)) + ; agdb only wants DMA out, no dma in + or.d IO_STATE (R_GEN_CONFIG, dma8, serial1),$r0 +#else or.d IO_STATE (R_GEN_CONFIG, dma9, serial1) \ | IO_STATE (R_GEN_CONFIG, dma8, serial1),$r0 #endif +#endif #ifdef CONFIG_DMA_MEMCPY ; 6/7 memory-memory DMA or.d IO_STATE (R_GEN_CONFIG, dma7, intdma6) \ @@ -561,19 +580,29 @@ 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 +#if (defined(CONFIG_ETRAX_AGDB)) + ; agdb only wants DMA out, no dma in + or.d IO_STATE (R_GEN_CONFIG, dma2, serial2),$r0 +#else or.d IO_STATE (R_GEN_CONFIG, dma3, serial2) \ | IO_STATE (R_GEN_CONFIG, dma2, serial2),$r0 #endif #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 +#if (defined(CONFIG_ETRAX_AGDB)) + ; agdb only wants DMA out, no dma in + or.d IO_STATE (R_GEN_CONFIG, dma4, serial3),$r0 +#else or.d IO_STATE (R_GEN_CONFIG, dma5, serial3) \ | IO_STATE (R_GEN_CONFIG, dma4, serial3),$r0 #endif #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 @@ -840,5 +869,5 @@ swapper_pg_dir = 0xc0002000 #endif - .section ".data.init" + .section ".data.init", "aw" #include "../lib/hw_settings.S" diff -urN linux-2.4.21-bk5/arch/cris/kernel/irq.c linux-2.4.21-bk6/arch/cris/kernel/irq.c --- linux-2.4.21-bk5/arch/cris/kernel/irq.c 2002-02-25 11:37:52.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/kernel/irq.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,8 +1,7 @@ -/* $Id: irq.c,v 1.18 2001/11/21 13:40:18 bjornw Exp $ - * +/* * linux/arch/cris/kernel/irq.c * - * Copyright (c) 2000,2001 Axis Communications AB + * Copyright (c) 2000, 2001, 2002, 2003 Axis Communications AB * * Authors: Bjorn Wesen (bjornw@axis.com) * @@ -59,7 +58,7 @@ disable_irq(unsigned int irq_nr) { unsigned long flags; - + save_flags(flags); cli(); mask_irq(irq_nr); @@ -70,6 +69,7 @@ enable_irq(unsigned int irq_nr) { unsigned long flags; + save_flags(flags); cli(); unmask_irq(irq_nr); @@ -88,7 +88,8 @@ return 0; } -irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */ +/* vector of shortcut jumps after the irq prologue */ +irqvectptr irq_shortcuts[NR_IRQS]; /* don't use set_int_vector, it bypasses the linux interrupt handlers. it is * global just so that the kernel gdb can use it. @@ -104,8 +105,8 @@ 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 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 */ @@ -117,7 +118,7 @@ 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; @@ -163,18 +164,18 @@ BUILD_IRQ(25, 0x2000000) /* IRQ 26-30 are reserved */ BUILD_IRQ(31, 0x80000000) - + /* - * Pointers to the low-level handlers + * 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, + 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 }; @@ -183,9 +184,9 @@ NULL, NULL, sIRQ2_interrupt, sIRQ3_interrupt, sIRQ4_interrupt, sIRQ5_interrupt, sIRQ6_interrupt, sIRQ7_interrupt, sIRQ8_interrupt, sIRQ9_interrupt, sIRQ10_interrupt, sIRQ11_interrupt, - sIRQ12_interrupt, sIRQ13_interrupt, NULL, NULL, - sIRQ16_interrupt, sIRQ17_interrupt, sIRQ18_interrupt, sIRQ19_interrupt, - sIRQ20_interrupt, sIRQ21_interrupt, sIRQ22_interrupt, sIRQ23_interrupt, + sIRQ12_interrupt, sIRQ13_interrupt, NULL, NULL, + sIRQ16_interrupt, sIRQ17_interrupt, sIRQ18_interrupt, sIRQ19_interrupt, + sIRQ20_interrupt, sIRQ21_interrupt, sIRQ22_interrupt, sIRQ23_interrupt, sIRQ24_interrupt, sIRQ25_interrupt, NULL, NULL, NULL, NULL, NULL, sIRQ31_interrupt }; @@ -230,7 +231,7 @@ for (i = 0; i < NR_IRQS; i++) { action = irq_action[i]; - if (!action) + if (!action) continue; len += sprintf(buf+len, "%2d: %10u %c %s", i, kstat.irqs[0][i], @@ -286,9 +287,9 @@ } /* this function links in a handler into the chain of handlers for the - given irq, and if the irq has never been registred, the appropriate - handler is entered into the interrupt vector -*/ + * given irq, and if the irq has never been registred, the appropriate + * handler is entered into the interrupt vector + */ int setup_etrax_irq(int irq, struct irqaction * new) { @@ -322,37 +323,39 @@ *p = new; if (!shared) { - /* if the irq wasn't registred before, enter it into the vector table - and unmask it physically - */ + /* if the irq wasn't registred before, enter it into the vector + * table and unmask it physically + */ set_int_vector(irq, interrupt[irq], sinterrupt[irq]); unmask_irq(irq); } - + restore_flags(flags); return 0; } /* this function is called by a driver to register an irq handler - Valid flags: - SA_INTERRUPT -> it's a fast interrupt, handler called with irq disabled and - no signal checking etc is performed upon exit - SA_SHIRQ -> the interrupt can be shared between different handlers, the handler - is required to check if the irq was "aimed" at it explicitely - SA_RANDOM -> the interrupt will add to the random generators entropy -*/ + * Valid flags: + * SA_INTERRUPT: it's a fast interrupt, handler called with irq disabled and + * no signal checking etc is performed upon exit + * SA_SHIRQ: the interrupt can be shared between different handlers, the + * handler is required to check if the irq was "aimed" at it + * explicitely + * SA_RANDOM: the interrupt will add to the random generators entropy + */ -int request_irq(unsigned int irq, +int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, + unsigned long irqflags, const char * devname, void *dev_id) { int retval; struct irqaction * action; - /* interrupts 0 and 1 are hardware breakpoint and NMI and we can't support - these yet. interrupt 15 is the multiple irq, it's special. */ + /* interrupts 0 and 1 are hardware breakpoint and NMI and we can't + * support these yet. interrupt 15 is the multiple irq, it's special. + */ if(irq < 2 || irq == 15 || irq >= NR_IRQS) return -EINVAL; @@ -362,7 +365,7 @@ /* allocate and fill in a handler structure and setup the irq */ - action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); + action = kmalloc(sizeof *action, GFP_KERNEL); if (!action) return -ENOMEM; @@ -379,7 +382,7 @@ kfree(action); return retval; } - + void free_irq(unsigned int irq, void *dev_id) { struct irqaction * action, **p; @@ -415,9 +418,9 @@ while(1); } -/* 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. -*/ +/* 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 system_call(void); /* from entry.S */ void do_sigtrap(void); /* from entry.S */ @@ -442,26 +445,27 @@ 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 < 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_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], 0); - + /* except IRQ 15 which is the multiple-IRQ handler on Etrax100 */ set_int_vector(15, multiple_interrupt, 0); - + /* 0 and 1 which are special breakpoint/NMI traps */ set_int_vector(0, hwbreakpoint, 0); @@ -475,17 +479,17 @@ 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); + /* 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 - } #if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL) diff -urN linux-2.4.21-bk5/arch/cris/kernel/kgdb.c linux-2.4.21-bk6/arch/cris/kernel/kgdb.c --- linux-2.4.21-bk5/arch/cris/kernel/kgdb.c 2002-11-28 15:53:09.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/kernel/kgdb.c 2003-07-10 02:54:58.000000000 -0700 @@ -18,6 +18,9 @@ *! Jul 21 1999 Bjorn Wesen eLinux port *! *! $Log: kgdb.c,v $ +*! Revision 1.8 2003/04/09 08:31:15 pkj +*! Typo correction (taken from Linux 2.5). +*! *! Revision 1.7 2002/07/12 09:14:56 bjornw *! Corrected typo *! @@ -58,7 +61,7 @@ *! *!--------------------------------------------------------------------------- *! -*! $Id: kgdb.c,v 1.7 2002/07/12 09:14:56 bjornw Exp $ +*! $Id: kgdb.c,v 1.8 2003/04/09 08:31:15 pkj Exp $ *! *! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN *! @@ -152,7 +155,7 @@ * (IPL too high, disabled, ...) * * - The gdb stub is currently not reentrant, i.e. errors that happen therein - * (e.g. accesing invalid memory) may not be caught correctly. This could + * (e.g. accessing invalid memory) may not be caught correctly. This could * be removed in future by introducing a stack of struct registers. * */ @@ -1486,7 +1489,7 @@ 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 its a ctrl-c +;; get the serial character (from debugport.c) and check if it is a ctrl-c jsr getDebugChar cmp.b 3, $r10 diff -urN linux-2.4.21-bk5/arch/cris/kernel/process.c linux-2.4.21-bk6/arch/cris/kernel/process.c --- linux-2.4.21-bk5/arch/cris/kernel/process.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/kernel/process.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.23 2002/10/14 18:29:27 johana Exp $ +/* $Id: process.c,v 1.24 2003/03/06 14:19:32 pkj Exp $ * * linux/arch/cris/kernel/process.c * @@ -8,6 +8,9 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: process.c,v $ + * Revision 1.24 2003/03/06 14:19:32 pkj + * Use a cpu_idle() function identical to the one used by i386. + * * Revision 1.23 2002/10/14 18:29:27 johana * Call etrax_gpio_wake_up_check() from cpu_idle() to reduce gpio latency * from ~15 ms to ~6 ms. @@ -114,7 +117,17 @@ * region by enable_hlt/disable_hlt. */ -static int hlt_counter=0; +static int hlt_counter; + +/* + * Powermanagement idle function, if any.. + */ +void (*pm_idle)(void); + +/* + * Power off function, if any + */ +void (*pm_power_off)(void); void disable_hlt(void) { @@ -125,19 +138,42 @@ { hlt_counter--; } -#ifdef CONFIG_ETRAX_GPIO -void etrax_gpio_wake_up_check(void); /* drivers/gpio.c */ -#endif -int cpu_idle(void *unused) +/* + * We use this if we don't have any better + * idle routine.. + */ +static void default_idle(void) { - while(1) { - current->counter = -100; #ifdef CONFIG_ETRAX_GPIO - /* This can reduce latency from 15 ms to 6 ms */ - etrax_gpio_wake_up_check(); /* drivers/gpio.c */ + extern void etrax_gpio_wake_up_check(void); /* drivers/gpio.c */ + + /* This can reduce latency from 15 ms to 6 ms */ + etrax_gpio_wake_up_check(); /* drivers/gpio.c */ #endif +} + +/* + * The idle thread. There's no useful work to be + * done, so just try to conserve power and have a + * low exit latency (ie sit in a loop waiting for + * somebody to say that they'd like to reschedule) + */ +void cpu_idle(void) +{ + /* endless idle loop with no priority at all */ + init_idle(); + current->nice = 20; + current->counter = -100; + + while(1) { + void (*idle)(void) = pm_idle; + if (!idle) + idle = default_idle; + while (!current->need_resched) + idle(); schedule(); + check_pgt_cache(); } } @@ -191,6 +227,8 @@ void machine_power_off(void) { + if (pm_power_off) + pm_power_off(); } /* diff -urN linux-2.4.21-bk5/arch/cris/kernel/setup.c linux-2.4.21-bk6/arch/cris/kernel/setup.c --- linux-2.4.21-bk5/arch/cris/kernel/setup.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/kernel/setup.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,9 +1,8 @@ -/* $Id: setup.c,v 1.27 2002/10/11 12:57:15 pkj Exp $ - * +/* * linux/arch/cris/kernel/setup.c * * Copyright (C) 1995 Linus Torvalds - * Copyright (c) 2001 Axis Communications AB + * Copyright (c) 2001, 2002, 2003 Axis Communications AB */ /* @@ -94,7 +93,8 @@ memory_start = (unsigned long) &_end; } else { /* otherwise the free area starts after the ROM filesystem */ - printk("ROM fs in RAM, size %lu bytes\n", romfs_length); + printk(KERN_INFO "ROM fs in RAM, size %lu bytes\n", + romfs_length); memory_start = romfs_start + romfs_length; } @@ -169,9 +169,14 @@ *cmdline_p = command_line; +#ifdef CONFIG_ETRAX_CMDLINE + strncpy(command_line, CONFIG_ETRAX_CMDLINE, COMMAND_LINE_SIZE); +#elif defined(CONFIG_ETRAX_ROOT_DEVICE) strncpy(command_line, "root=", COMMAND_LINE_SIZE); strncpy(command_line+5, CONFIG_ETRAX_ROOT_DEVICE, COMMAND_LINE_SIZE-5); +#endif + command_line[COMMAND_LINE_SIZE - 1] = '\0'; /* Save command line copy for /proc/cmdline */ @@ -180,7 +185,7 @@ /* give credit for the CRIS port */ - printk("Linux/CRIS port on ETRAX 100LX (c) 2001, 2002 Axis Communications AB\n"); + printk(KERN_INFO "Linux/CRIS port on ETRAX 100LX (c) 2001, 2002 Axis Communications AB\n"); } diff -urN linux-2.4.21-bk5/arch/cris/kernel/shadows.c linux-2.4.21-bk6/arch/cris/kernel/shadows.c --- linux-2.4.21-bk5/arch/cris/kernel/shadows.c 2001-05-01 16:04:56.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/kernel/shadows.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,6 +1,5 @@ -/* $Id: shadows.c,v 1.2 2001/03/15 14:25:16 bjornw Exp $ - * - * Various shadow registers. Defines for these are in include/asm-etrax100/io.h +/* + * Various shadow registers. Defines for these are in include/asm-cris/io.h */ /* Shadows for internal Etrax-registers */ diff -urN linux-2.4.21-bk5/arch/cris/kernel/signal.c linux-2.4.21-bk6/arch/cris/kernel/signal.c --- linux-2.4.21-bk5/arch/cris/kernel/signal.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/kernel/signal.c 2003-07-10 02:54:58.000000000 -0700 @@ -528,7 +528,7 @@ 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 dont restart */ + we only get here if there is a handler, we don't restart */ regs->r10 = -EINTR; break; diff -urN linux-2.4.21-bk5/arch/cris/kernel/sys_cris.c linux-2.4.21-bk6/arch/cris/kernel/sys_cris.c --- linux-2.4.21-bk5/arch/cris/kernel/sys_cris.c 2001-07-26 15:10:06.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/kernel/sys_cris.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,5 +1,4 @@ -/* $Id: sys_cris.c,v 1.10 2001/06/27 21:16:15 hp Exp $ - * +/* * linux/arch/cris/kernel/sys_cris.c * * This file contains various random system calls that diff -urN linux-2.4.21-bk5/arch/cris/kernel/time.c linux-2.4.21-bk6/arch/cris/kernel/time.c --- linux-2.4.21-bk5/arch/cris/kernel/time.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/kernel/time.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,9 +1,8 @@ -/* $Id: time.c,v 1.17 2002/11/15 14:49:24 oskarp Exp $ - * +/* * linux/arch/cris/kernel/time.c * * Copyright (C) 1991, 1992, 1995 Linus Torvalds - * Copyright (C) 1999, 2000, 2001, 2002 Axis Communications AB + * Copyright (C) 1999, 2000, 2001, 2002, 2003 Axis Communications AB * * 1994-07-02 Alan Modra * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime @@ -18,15 +17,16 @@ * Linux/CRIS specific code: * * Authors: Bjorn Wesen - * Johan Adolfsson + * Johan Adolfsson + * * 2002-03-04 Johan Adolfsson - * Use prescale timer at 25000 Hz instead of the baudrate timer at - * 19200 to get rid of the 64ppm to fast timer (and we get better + * Use prescale timer at 25000 Hz instead of the baudrate timer at + * 19200 to get rid of the 64ppm to fast timer (and we get better * resolution within a jiffie as well. * 2002-03-05 Johan Adolfsson * Use prescaler in do_slow_gettimeoffset() to get 1 us resolution (40ns) * 2002-09-06 Johan Adolfsson - * Handle lost ticks by checking wall_jiffies, more efficient code + * Handle lost ticks by checking wall_jiffies, more efficient code * by using local vars and not the pointer argument. * */ @@ -103,6 +103,60 @@ } +/* Convert the clkdiv_low and clkdivb_high fiels in timer_data + * (from *R_TIMER_DATA) to nanoseconds (67 ns resolution) + */ +unsigned long timer_data_to_ns(unsigned long timer_data) +{ +/* low (clkdiv_low lsb toggles with 7.3728MHz so it counts + * with 14.7456 MHz = 67.816 ns (0-17361ns) + * high (clkdiv_high lsb toggles with 38.4kHz so it counts + * with 76.8kHz = 13020.833 ns (0-3333333 ns) + * By checking bit 9,8,7 we can now how to compensate the low value + * to get a 67 ns resolution all the way. +Example of R_TIMER_DATA values: + bit 98 7 low 9 87 offset +0289DC00 00 000 0 0 00 0 +0289DC41 00 010 64 0 01 0 +0289DC81 00 100 128 0 10 0 +0289DDC0 01 110 192 1 11 0 13020 = 192 +0289DD01 01 000 0 256 1 00 +256 +0289DD41 01 010 64 320 1 01 +256 + +0288DE80 10 100 128 384 0 10 0: -128 26040= 384 +0288DEC1 10 110 192 448 0 11 64 -128 +0288DE01 10 000 0 512 128 +128 +0288DF40 11 010 64 576 192 +128 39060 +0288DF81 11 100 128 640 256 +128 +0288DFC1 11 110 192 704 320 +128 + ..393 +*/ + + static const short timer_data_add[8] = { + 0, /* 00 0 */ + 0, /* 00 1 */ + 256, /* 01 0 */ + 0, /* 01 1 */ + 128, /* 10 0 */ + -128,/* 10 1 */ + 128, /* 11 0 */ + 128 /* 11 1 */ + }; + unsigned long ns; + unsigned long low; + unsigned long high; + + high = (((timer_data & 0x0000FE00)>>8) * 13020833)/1000; + ns = high; + + low = timer_data & 0xFF; + low += timer_data_add[(timer_data >>7) & 0x7]; + ns += (low * 67816)/1000; + return ns; +} /* timer_data_to_ns */ + + + #if CRIS_TEST_TIMERS #define NS_TEST_SIZE 4000 @@ -274,7 +328,7 @@ int retval = 0; int real_seconds, real_minutes, cmos_minutes; - printk("set_rtc_mmss(%lu)\n", nowtime); + printk(KERN_INFO "set_rtc_mmss(%lu)\n", nowtime); if(!have_rtc) return 0; @@ -460,7 +514,8 @@ mon = CMOS_READ(RTC_MONTH); year = CMOS_READ(RTC_YEAR); - printk("rtc: sec 0x%x min 0x%x hour 0x%x day 0x%x mon 0x%x year 0x%x\n", + printk(KERN_INFO + "rtc: sec 0x%x min 0x%x hour 0x%x day 0x%x mon 0x%x year 0x%x\n", sec, min, hour, day, mon, year); BCD_TO_BIN(sec); @@ -585,7 +640,7 @@ /* enable watchdog if we should use one */ #if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) - printk("Enabling watchdog...\n"); + printk(KERN_INFO "Enabling watchdog...\n"); start_watchdog(); /* If we use the hardware watchdog, we want to trap it as an NMI diff -urN linux-2.4.21-bk5/arch/cris/kernel/traps.c linux-2.4.21-bk6/arch/cris/kernel/traps.c --- linux-2.4.21-bk5/arch/cris/kernel/traps.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/kernel/traps.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,12 +1,11 @@ -/* $Id: traps.c,v 1.18 2002/12/02 08:11:41 starvik Exp $ - * +/* * linux/arch/cris/traps.c * - * Here we handle the break vectors not used by the system call - * mechanism, as well as some general stack/register dumping + * Here we handle the break vectors not used by the system call + * mechanism, as well as some general stack/register dumping * things. - * - * Copyright (C) 2000,2001 Axis Communications AB + * + * Copyright (C) 2000, 2001, 2002, 2003 Axis Communications AB * * Authors: Bjorn Wesen * Hans-Peter Nilsson @@ -216,6 +215,8 @@ * 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) @@ -239,9 +240,6 @@ void die_if_kernel(const char * str, struct pt_regs * regs, long err) { - extern void reset_watchdog(void); - extern void stop_watchdog(void); - if(user_mode(regs)) return; diff -urN linux-2.4.21-bk5/arch/cris/lib/checksum.S linux-2.4.21-bk6/arch/cris/lib/checksum.S --- linux-2.4.21-bk5/arch/cris/lib/checksum.S 2001-10-08 11:43:54.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/lib/checksum.S 2003-07-10 02:54:58.000000000 -0700 @@ -1,6 +1,6 @@ -/* $Id: checksum.S,v 1.6 2001/10/01 14:47:35 bjornw Exp $ +/* * A fast checksum routine using movem - * Copyright (c) 1998-2001 Axis Communications AB + * Copyright (c) 1998-2003 Axis Communications AB * * csum_partial(const unsigned char * buff, int len, unsigned int sum) */ diff -urN linux-2.4.21-bk5/arch/cris/lib/checksumcopy.S linux-2.4.21-bk6/arch/cris/lib/checksumcopy.S --- linux-2.4.21-bk5/arch/cris/lib/checksumcopy.S 2001-10-08 11:43:54.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/lib/checksumcopy.S 2003-07-10 02:54:58.000000000 -0700 @@ -1,9 +1,9 @@ -/* $Id: checksumcopy.S,v 1.7 2001/10/01 14:47:35 bjornw Exp $ +/* * A fast checksum+copy routine using movem - * Copyright (c) 1998, 2001 Axis Communications AB + * Copyright (c) 1998-2003 Axis Communications AB * * Authors: Bjorn Wesen - * + * * csum_partial_copy_nocheck(const char *src, char *dst, * int len, unsigned int sum) */ diff -urN linux-2.4.21-bk5/arch/cris/lib/dmacopy.c linux-2.4.21-bk6/arch/cris/lib/dmacopy.c --- linux-2.4.21-bk5/arch/cris/lib/dmacopy.c 2001-02-08 16:32:44.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/lib/dmacopy.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,5 +1,4 @@ -/* $Id: dmacopy.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $ - * +/* * memcpy for large blocks, using memory-memory DMA channels 6 and 7 in Etrax */ @@ -13,11 +12,11 @@ unsigned int pn) { static etrax_dma_descr indma, outdma; - + D(printk("dma_memcpy %d bytes... ", pn)); #if 0 - *R_GEN_CONFIG = genconfig_shadow = + *R_GEN_CONFIG = genconfig_shadow = (genconfig_shadow & ~0x3c0000) | IO_STATE(R_GEN_CONFIG, dma6, intdma7) | IO_STATE(R_GEN_CONFIG, dma7, intdma6); @@ -32,12 +31,9 @@ *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 -urN linux-2.4.21-bk5/arch/cris/lib/dram_init.S linux-2.4.21-bk6/arch/cris/lib/dram_init.S --- linux-2.4.21-bk5/arch/cris/lib/dram_init.S 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/lib/dram_init.S 2003-07-10 02:54:58.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: dram_init.S,v 1.13 2002/10/30 07:42:28 starvik Exp $ +/* $Id: dram_init.S,v 1.14 2003/03/31 07:07:08 starvik Exp $ * * DRAM/SDRAM initialization - alter with care * This file is intended to be included from other assembler files @@ -11,6 +11,9 @@ * Authors: Mikael Starvik (starvik@axis.com) * * $Log: dram_init.S,v $ + * Revision 1.14 2003/03/31 07:07:08 starvik + * Corrected calculation of end of sdram init commands + * * Revision 1.13 2002/10/30 07:42:28 starvik * Always read SDRAM command sequence from flash * @@ -148,7 +151,7 @@ move.d _sdram_commands_start, $r2 and.d 0x00ffffff, $r2 ; Make sure commands are read from flash move.d _sdram_commands_end, $r3 - and.d 0x00ffffff, $r2 + and.d 0x00ffffff, $r3 1: clear.d $r4 move.b [$r2+], $r4 lslq 9, $r4 ; Command starts at bit 9 diff -urN linux-2.4.21-bk5/arch/cris/lib/hw_settings.S linux-2.4.21-bk6/arch/cris/lib/hw_settings.S --- linux-2.4.21-bk5/arch/cris/lib/hw_settings.S 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/lib/hw_settings.S 2003-07-10 02:54:58.000000000 -0700 @@ -1,13 +1,11 @@ /* - * $Id: hw_settings.S,v 1.5 2002/08/15 09:16:06 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, 2002 Axis Communications AB + * Copyright (C) 2001, 2002, 2003 Axis Communications AB * - * Authors: Mikael Starvik (starvik@axis.com) + * Authors: Mikael Starvik (starvik@axis.com) */ #define PA_SET_VALUE ((CONFIG_ETRAX_DEF_R_PORT_PA_DIR << 8) | \ @@ -15,13 +13,13 @@ #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 ; Where to load the kernel image (physical!) ; Debug port #ifdef CONFIG_ETRAX_DEBUG_PORT0 - .dword 0 + .dword 0 #elif defined(CONFIG_ETRAX_DEBUG_PORT1) .dword 1 #elif defined(CONFIG_ETRAX_DEBUG_PORT2) @@ -30,7 +28,7 @@ .dword 3 #else .dword 4 ; No debug -#endif +#endif ; SDRAM or EDO DRAM? #ifdef CONFIG_ETRAX_SDRAM @@ -39,7 +37,7 @@ .dword 0 #endif - ; Register values + ; Register values .dword R_WAITSTATES .dword CONFIG_ETRAX_DEF_R_WAITSTATES .dword R_BUS_CONFIG @@ -56,7 +54,7 @@ .dword CONFIG_ETRAX_DEF_R_DRAM_TIMING #endif .dword R_PORT_PA_SET - .dword PA_SET_VALUE + .dword PA_SET_VALUE .dword R_PORT_PB_SET .dword PB_SET_VALUE .dword 0 ; No more register values diff -urN linux-2.4.21-bk5/arch/cris/lib/old_checksum.c linux-2.4.21-bk6/arch/cris/lib/old_checksum.c --- linux-2.4.21-bk5/arch/cris/lib/old_checksum.c 2001-02-08 16:32:44.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/lib/old_checksum.c 2003-07-10 02:54:58.000000000 -0700 @@ -1,5 +1,4 @@ -/* $Id: old_checksum.c,v 1.1 2000/07/10 16:25:21 bjornw 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. diff -urN linux-2.4.21-bk5/arch/cris/mm/fault.c linux-2.4.21-bk6/arch/cris/mm/fault.c --- linux-2.4.21-bk5/arch/cris/mm/fault.c 2002-11-28 15:53:09.000000000 -0800 +++ linux-2.4.21-bk6/arch/cris/mm/fault.c 2003-07-10 02:54:58.000000000 -0700 @@ -6,6 +6,10 @@ * Authors: Bjorn Wesen * * $Log: fault.c,v $ + * Revision 1.22 2003/07/07 09:07:04 johana + * Added special CONFIG_ETRAX_DEBUG_INTERRUPT handling here + * to deal with a di in entry.S + * * Revision 1.21 2002/05/28 14:24:56 bjornw * Corrected typo * @@ -68,6 +72,7 @@ * */ +#include #include #include #include @@ -126,6 +131,9 @@ int errcode; unsigned long address; +#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT /* The di is actually in entry.S */ + log_int(rdpc(), regs->dccr, 0); +#endif cause = *R_MMU_CAUSE; select = *R_TLB_SELECT; diff -urN linux-2.4.21-bk5/arch/cris/mm/init.c linux-2.4.21-bk6/arch/cris/mm/init.c --- linux-2.4.21-bk5/arch/cris/mm/init.c 2002-08-02 17:39:42.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/mm/init.c 2003-07-10 02:54:58.000000000 -0700 @@ -7,6 +7,15 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: init.c,v $ + * Revision 1.38 2003/04/01 14:12:08 starvik + * Added loglevel for lots of printks + * + * Revision 1.37 2003/01/22 06:54:47 starvik + * Fixed warnings issued by GCC 3.2.1 + * + * Revision 1.36 2003/01/09 17:59:55 starvik + * Added init_ioremap to initcalls + * * Revision 1.35 2002/05/17 05:33:59 starvik * Limit cache flush range to the size of the cache * @@ -113,6 +122,7 @@ #include #include #include +#include #include #include @@ -208,7 +218,7 @@ int i; unsigned long zones_size[MAX_NR_ZONES]; - printk("Setting up paging and the MMU.\n"); + printk(KERN_INFO "Setting up paging and the MMU.\n"); /* clear out the init_mm.pgd that will contain the kernel's mappings */ @@ -404,7 +414,8 @@ datasize = (unsigned long) &_edata - (unsigned long) &_etext; initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; - printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, " + printk(KERN_INFO + "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, " "%dk init)\n" , (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), max_mapnr << (PAGE_SHIFT-10), @@ -425,47 +436,40 @@ return; } -/* Initialize remaps of some I/O-ports. This is designed to be callable - * multiple times from the drivers init-sections, because we don't know - * beforehand which driver will get initialized first. +/* Initialize remaps of some I/O-ports. It is important that this + * is called before any driver is initialized. */ -void -init_ioremap(void) +static int +__init init_ioremap(void) { /* Give the external I/O-port addresses their values */ - static int initialized = 0; - - if( !initialized ) { - initialized++; - #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 - } + /* 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 @@ -481,7 +485,7 @@ length = length > 8192 ? 8192 : length; /* No need to flush more than cache size */ while(length > 0) { - short tmp = *flushadr; /* dummy read to flush */ + *flushadr; /* dummy read to flush */ flushadr += (32/sizeof(short)); /* a cacheline is 32 bytes */ length -= 32; } @@ -524,7 +528,7 @@ totalram_pages++; } printk (KERN_INFO "Freeing unused kernel memory: %luk freed\n", - (&__init_end - &__init_begin) >> 10); + (unsigned long)((&__init_end - &__init_begin) >> 10)); } void diff -urN linux-2.4.21-bk5/arch/cris/mm/ioremap.c linux-2.4.21-bk6/arch/cris/mm/ioremap.c --- linux-2.4.21-bk5/arch/cris/mm/ioremap.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.21-bk6/arch/cris/mm/ioremap.c 2003-07-10 02:54:58.000000000 -0700 @@ -146,7 +146,7 @@ */ offset = phys_addr & ~PAGE_MASK; phys_addr &= PAGE_MASK; - size = PAGE_ALIGN(last_addr) - phys_addr; + size = PAGE_ALIGN(last_addr+1) - phys_addr; /* * Ok, go for it.. diff -urN linux-2.4.21-bk5/arch/i386/kernel/acpi.c linux-2.4.21-bk6/arch/i386/kernel/acpi.c --- linux-2.4.21-bk5/arch/i386/kernel/acpi.c 2003-07-10 02:54:54.000000000 -0700 +++ linux-2.4.21-bk6/arch/i386/kernel/acpi.c 2003-07-10 02:54:58.000000000 -0700 @@ -129,6 +129,8 @@ printk(KERN_INFO PREFIX "Local APIC address 0x%08x\n", madt->lapic_address); + acpi_madt_oem_check(madt->header.oem_id, madt->header.oem_table_id); + return 0; } diff -urN linux-2.4.21-bk5/arch/i386/kernel/io_apic.c linux-2.4.21-bk6/arch/i386/kernel/io_apic.c --- linux-2.4.21-bk5/arch/i386/kernel/io_apic.c 2003-07-10 02:54:54.000000000 -0700 +++ linux-2.4.21-bk6/arch/i386/kernel/io_apic.c 2003-07-10 02:54:58.000000000 -0700 @@ -1732,6 +1732,13 @@ apic_id = reg_00.ID; } + /* XAPICs do not need unique IDs */ + if (clustered_apic_mode == CLUSTERED_APIC_XAPIC){ + printk(KERN_INFO "IOAPIC[%d]: Assigned apic_id %d\n", + ioapic, apic_id); + return apic_id; + } + /* * Every APIC in a system must have a unique ID or we get lots of nice * 'stuck on smp_invalidate_needed IPI wait' messages. diff -urN linux-2.4.21-bk5/arch/i386/kernel/mpparse.c linux-2.4.21-bk6/arch/i386/kernel/mpparse.c --- linux-2.4.21-bk5/arch/i386/kernel/mpparse.c 2003-07-10 02:54:54.000000000 -0700 +++ linux-2.4.21-bk6/arch/i386/kernel/mpparse.c 2003-07-10 02:54:58.000000000 -0700 @@ -1252,6 +1252,23 @@ io_apic_set_pci_routing(ioapic, ioapic_pin, irq); } +/* Hook from generic ACPI tables.c */ +void __init acpi_madt_oem_check(char *oem_id, char *oem_table_id) +{ + if (!strncmp(oem_id, "IBM", 3) && + (!strncmp(oem_table_id, "SERVIGIL", 8) || + !strncmp(oem_table_id, "EXA", 3) || + !strncmp(oem_table_id, "RUTHLESS", 8))){ + clustered_apic_mode = CLUSTERED_APIC_XAPIC; + apic_broadcast_id = APIC_BROADCAST_ID_XAPIC; + int_dest_addr_mode = APIC_DEST_PHYSICAL; + int_delivery_mode = dest_Fixed; + esr_disable = 1; + /*Start cyclone clock*/ + cyclone_setup(0); + } +} + #ifdef CONFIG_ACPI_PCI void __init mp_parse_prt (void) diff -urN linux-2.4.21-bk5/arch/ppc/boot/chrp/main.c linux-2.4.21-bk6/arch/ppc/boot/chrp/main.c --- linux-2.4.21-bk5/arch/ppc/boot/chrp/main.c 2003-06-13 07:51:31.000000000 -0700 +++ linux-2.4.21-bk6/arch/ppc/boot/chrp/main.c 2003-07-10 02:54:58.000000000 -0700 @@ -43,6 +43,8 @@ static char scratch[SCRATCH_SIZE]; /* 1MB of scratch space for gunzip */ +typedef void (*kernel_start_t)(int, int, void *, unsigned int, unsigned int); + void chrpboot(int a1, int a2, void *prom) { @@ -93,7 +95,7 @@ sa = (unsigned long)PROG_START; printf("start address = 0x%x\n\r", sa); - (*(void (*)())sa)(a1, a2, prom, initrd_start, initrd_size); + (*(kernel_start_t)sa)(a1, a2, prom, initrd_start, initrd_size); printf("returned?\n\r"); diff -urN linux-2.4.21-bk5/arch/ppc/boot/common/Makefile linux-2.4.21-bk6/arch/ppc/boot/common/Makefile --- linux-2.4.21-bk5/arch/ppc/boot/common/Makefile 2002-08-02 17:39:43.000000000 -0700 +++ linux-2.4.21-bk6/arch/ppc/boot/common/Makefile 2003-07-10 02:54:58.000000000 -0700 @@ -11,7 +11,6 @@ USE_STANDARD_AS_RULE := true coffcrt0.o: - $(CC) -I$(TOPDIR)/arch/$(ARCH)/kernel $(AFLAGS) -DXCOFF \ - -traditional -c -o coffcrt0.o crt0.S + $(CC) $(AFLAGS) -DXCOFF -traditional -c -o coffcrt0.o crt0.S include $(TOPDIR)/Rules.make diff -urN linux-2.4.21-bk5/arch/ppc/boot/pmac/Makefile linux-2.4.21-bk6/arch/ppc/boot/pmac/Makefile --- linux-2.4.21-bk5/arch/ppc/boot/pmac/Makefile 2003-06-13 07:51:31.000000000 -0700 +++ linux-2.4.21-bk6/arch/ppc/boot/pmac/Makefile 2003-07-10 02:54:58.000000000 -0700 @@ -30,8 +30,6 @@ TFTPIMAGE=/tftpboot/zImage.pmac$(END) -AFLAGS_../common/crt0.o += -I$(TOPDIR)/arch/$(ARCH)/kernel - ../common/coffcrt0.o: $(MAKE) -C ../common coffcrt0.o diff -urN linux-2.4.21-bk5/arch/ppc/boot/pmac/chrpmain.c linux-2.4.21-bk6/arch/ppc/boot/pmac/chrpmain.c --- linux-2.4.21-bk5/arch/ppc/boot/pmac/chrpmain.c 2003-06-13 07:51:31.000000000 -0700 +++ linux-2.4.21-bk6/arch/ppc/boot/pmac/chrpmain.c 2003-07-10 02:54:58.000000000 -0700 @@ -38,6 +38,8 @@ #define SCRATCH_SIZE (128 << 10) +typedef void (*kernel_start_t)(int, int, void *); + void boot(int a1, int a2, void *prom) { unsigned sa, len; @@ -87,7 +89,7 @@ sa = (unsigned long)PROG_START; printf("start address = 0x%x\n", sa); - (*(void (*)())sa)(a1, a2, prom); + (*(kernel_start_t)sa)(a1, a2, prom); printf("returned?\n"); diff -urN linux-2.4.21-bk5/arch/ppc/boot/pmac/coffmain.c linux-2.4.21-bk6/arch/ppc/boot/pmac/coffmain.c --- linux-2.4.21-bk5/arch/ppc/boot/pmac/coffmain.c 2003-06-13 07:51:31.000000000 -0700 +++ linux-2.4.21-bk6/arch/ppc/boot/pmac/coffmain.c 2003-07-10 02:54:58.000000000 -0700 @@ -44,6 +44,8 @@ static char heap[SCRATCH_SIZE]; +typedef void (*kernel_start_t)(int, int, void *); + void boot(int a1, int a2, void *prom) { unsigned sa, len; @@ -92,7 +94,7 @@ sa = (unsigned long)PROG_START; printf("start address = 0x%x\n", sa); - (*(void (*)())sa)(a1, a2, prom); + (*(kernel_start_t)sa)(a1, a2, prom); printf("returned?\n"); diff -urN linux-2.4.21-bk5/arch/ppc/boot/pmac/start.c linux-2.4.21-bk6/arch/ppc/boot/pmac/start.c --- linux-2.4.21-bk5/arch/ppc/boot/pmac/start.c 2003-06-13 07:51:31.000000000 -0700 +++ linux-2.4.21-bk6/arch/ppc/boot/pmac/start.c 2003-07-10 02:54:58.000000000 -0700 @@ -11,7 +11,7 @@ extern int strlen(const char *s); extern void boot(int a1, int a2, void *prom); -int (*prom)(); +int (*prom)(void *args); void *chosen_handle; void *stdin; @@ -26,7 +26,7 @@ void start(int a1, int a2, void *promptr) { - prom = (int (*)()) promptr; + prom = (int (*)(void *)) promptr; chosen_handle = finddevice("/chosen"); if (chosen_handle == (void *) -1) exit(); diff -urN linux-2.4.21-bk5/arch/ppc/boot/prep/Makefile linux-2.4.21-bk6/arch/ppc/boot/prep/Makefile --- linux-2.4.21-bk5/arch/ppc/boot/prep/Makefile 2003-06-13 07:51:31.000000000 -0700 +++ linux-2.4.21-bk6/arch/ppc/boot/prep/Makefile 2003-07-10 02:54:58.000000000 -0700 @@ -39,10 +39,6 @@ # Extra include search dirs CFLAGS_kbd.o += -I$(TOPDIR)/drivers/char -AFLAGS_head.o += -I$(TOPDIR)/arch/$(ARCH)/kernel -AFLAGS_../common/util.o += -I$(TOPDIR)/arch/$(ARCH)/kernel -AFLAGS_../common/relocate.o += -I$(TOPDIR)/arch/$(ARCH)/kernel -AFLAGS_../simple/legacy.o += -I$(TOPDIR)/arch/$(ARCH)/kernel all: zImage diff -urN linux-2.4.21-bk5/arch/ppc/kernel/setup.c linux-2.4.21-bk6/arch/ppc/kernel/setup.c --- linux-2.4.21-bk5/arch/ppc/kernel/setup.c 2003-06-13 07:51:31.000000000 -0700 +++ linux-2.4.21-bk6/arch/ppc/kernel/setup.c 2003-07-10 02:54:58.000000000 -0700 @@ -89,7 +89,7 @@ int icache_bsize; int ucache_bsize; -#ifdef CONFIG_VGA_CONSOLE +#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_FB) struct screen_info screen_info = { 0, 25, /* orig-x, orig-y */ 0, /* unused */ @@ -101,7 +101,7 @@ 1, /* orig-video-isVGA */ 16 /* orig-video-points */ }; -#endif /* CONFIG_VGA_CONSOLE */ +#endif /* CONFIG_VGA_CONSOLE || CONFIG_FB */ void machine_restart(char *cmd) { diff -urN linux-2.4.21-bk5/arch/ppc/platforms/prep_setup.c linux-2.4.21-bk6/arch/ppc/platforms/prep_setup.c --- linux-2.4.21-bk5/arch/ppc/platforms/prep_setup.c 2003-06-13 07:51:31.000000000 -0700 +++ linux-2.4.21-bk6/arch/ppc/platforms/prep_setup.c 2003-07-10 02:54:58.000000000 -0700 @@ -659,8 +659,7 @@ prep_init_vesa(void) { #if defined(CONFIG_PREP_RESIDUAL) && \ - (defined(CONFIG_FB_VGA16) || defined(CONFIG_FB_VGA_16_MODULE) || \ - defined(CONFIG_FB_VESA)) + (defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_FB)) PPC_DEVICE *vgadev; vgadev = residual_find_device(~0, NULL, DisplayController, SVGAController, diff -urN linux-2.4.21-bk5/arch/sparc/kernel/sys_sparc.c linux-2.4.21-bk6/arch/sparc/kernel/sys_sparc.c --- linux-2.4.21-bk5/arch/sparc/kernel/sys_sparc.c 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.21-bk6/arch/sparc/kernel/sys_sparc.c 2003-07-10 02:54:58.000000000 -0700 @@ -123,7 +123,10 @@ if (call <= SEMCTL) switch (call) { case SEMOP: - err = sys_semop (first, (struct sembuf *)ptr, second); + err = sys_semtimedop (first, (struct sembuf *)ptr, second, NULL); + goto out; + case SEMTIMEDOP: + err = sys_semtimedop (first, (struct sembuf *)ptr, second, (const struct timespec *) fifth); goto out; case SEMGET: err = sys_semget (first, second, third); diff -urN linux-2.4.21-bk5/arch/sparc64/kernel/sys_sparc.c linux-2.4.21-bk6/arch/sparc64/kernel/sys_sparc.c --- linux-2.4.21-bk5/arch/sparc64/kernel/sys_sparc.c 2003-07-10 02:54:55.000000000 -0700 +++ linux-2.4.21-bk6/arch/sparc64/kernel/sys_sparc.c 2003-07-10 02:54:58.000000000 -0700 @@ -182,7 +182,10 @@ if (call <= SEMCTL) switch (call) { case SEMOP: - err = sys_semop (first, (struct sembuf *)ptr, second); + err = sys_semtimedop (first, (struct sembuf *)ptr, second, NULL); + goto out; + case SEMTIMEDOP: + err = sys_semtimedop (first, (struct sembuf *)ptr, second, (const struct timespec *) fifth); goto out; case SEMGET: err = sys_semget (first, second, (int)third); diff -urN linux-2.4.21-bk5/arch/sparc64/kernel/sys_sparc32.c linux-2.4.21-bk6/arch/sparc64/kernel/sys_sparc32.c --- linux-2.4.21-bk5/arch/sparc64/kernel/sys_sparc32.c 2003-07-10 02:54:55.000000000 -0700 +++ linux-2.4.21-bk6/arch/sparc64/kernel/sys_sparc32.c 2003-07-10 02:54:58.000000000 -0700 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -762,6 +763,38 @@ return err; } +static __inline__ void *alloc_user_space(long len) +{ + struct pt_regs *regs = current->thread.kregs; + unsigned long usp = regs->u_regs[UREG_I6]; + + if (!(current->thread.flags & SPARC_FLAG_32BIT)) + usp += STACK_BIAS; + + return (void *) (usp - len); +} + +struct timespec32 { + s32 tv_sec; + s32 tv_nsec; +}; + +static int sys32_semtimedop(int semid, struct sembuf *tsems, int nsems, + const struct timespec32 *timeout32) +{ + struct timespec32 t32; + struct timespec *t64 = alloc_user_space(sizeof(*t64)); + + if (copy_from_user(&t32, timeout32, sizeof(t32))) + return -EFAULT; + + if (put_user(t32.tv_sec, &t64->tv_sec) || + put_user(t32.tv_nsec, &t64->tv_nsec)) + return -EFAULT; + + return sys_semtimedop(semid, tsems, nsems, t64); +} + asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth) { int version, err; @@ -773,8 +806,10 @@ switch (call) { case SEMOP: /* struct sembuf is the same on 32 and 64bit :)) */ - err = sys_semop (first, (struct sembuf *)AA(ptr), second); + err = sys_semtimedop (first, (struct sembuf *)AA(ptr), second, NULL); goto out; + case SEMTIMEDOP: + err = sys32_semtimedop (first, (struct sembuf *)AA(ptr), second, (const struct timespec32 *) AA(fifth)); case SEMGET: err = sys_semget (first, second, third); goto out; @@ -1945,11 +1980,6 @@ return ret; } -struct timespec32 { - s32 tv_sec; - s32 tv_nsec; -}; - extern asmlinkage int sys_sched_rr_get_interval(pid_t pid, struct timespec *interval); asmlinkage int sys32_sched_rr_get_interval(__kernel_pid_t32 pid, struct timespec32 *interval) diff -urN linux-2.4.21-bk5/drivers/char/drm/drm_agpsupport.h linux-2.4.21-bk6/drivers/char/drm/drm_agpsupport.h --- linux-2.4.21-bk5/drivers/char/drm/drm_agpsupport.h 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.21-bk6/drivers/char/drm/drm_agpsupport.h 2003-07-10 02:54:59.000000000 -0700 @@ -283,8 +283,15 @@ break; case VIA_APOLLO_PRO: head->chipset = "VIA Apollo Pro"; break; +/* + * Alan merged agp_backend.h which removes VIA_APOLLO_P4X400, but + * didnt update this file. I just commented it out so we dont + * get compile errors on -pre4 and it can be fixed in -pre5. + * case VIA_APOLLO_P4X400: head->chipset = "VIA Apollo P4X400"; break; + +*/ case SIS_GENERIC: head->chipset = "SiS"; break; case AMD_GENERIC: head->chipset = "AMD"; break; diff -urN linux-2.4.21-bk5/fs/Config.in linux-2.4.21-bk6/fs/Config.in --- linux-2.4.21-bk5/fs/Config.in 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/fs/Config.in 2003-07-10 02:55:00.000000000 -0700 @@ -19,13 +19,13 @@ dep_tristate 'Amiga FFS file system support (EXPERIMENTAL)' CONFIG_AFFS_FS $CONFIG_EXPERIMENTAL -dep_tristate 'Apple Macintosh file system support (EXPERIMENTAL)' CONFIG_HFS_FS $CONFIG_EXPERIMENTAL +dep_tristate 'Apple HFS file system support (EXPERIMENTAL)' CONFIG_HFS_FS $CONFIG_EXPERIMENTAL + +dep_tristate 'Apple HFS+ (Extended HFS) file system support (EXPERIMENTAL)' CONFIG_HFSPLUS_FS $CONFIG_EXPERIMENTAL dep_tristate 'BeOS file systemv(BeFS) support (read only) (EXPERIMENTAL)' CONFIG_BEFS_FS $CONFIG_EXPERIMENTAL dep_mbool ' Debug Befs' CONFIG_BEFS_DEBUG $CONFIG_BEFS_FS -dep_tristate 'Apple Extended HFS file system support (EXPERIMENTAL)' CONFIG_HFSPLUS_FS $CONFIG_EXPERIMENTAL - dep_tristate 'BFS file system support (EXPERIMENTAL)' CONFIG_BFS_FS $CONFIG_EXPERIMENTAL tristate 'Ext3 journalling file system support' CONFIG_EXT3_FS @@ -106,7 +106,6 @@ dep_tristate 'InterMezzo file system support (replicating fs) (EXPERIMENTAL)' CONFIG_INTERMEZZO_FS $CONFIG_INET $CONFIG_EXPERIMENTAL dep_tristate 'NFS file system support' CONFIG_NFS_FS $CONFIG_INET dep_mbool ' Provide NFSv3 client support' CONFIG_NFS_V3 $CONFIG_NFS_FS - dep_mbool ' Allow direct I/O on NFS files (EXPERIMENTAL)' CONFIG_NFS_DIRECTIO $CONFIG_NFS_FS $CONFIG_EXPERIMENTAL dep_bool ' Root file system on NFS' CONFIG_ROOT_NFS $CONFIG_NFS_FS $CONFIG_IP_PNP dep_tristate 'NFS server support' CONFIG_NFSD $CONFIG_INET diff -urN linux-2.4.21-bk5/fs/block_dev.c linux-2.4.21-bk6/fs/block_dev.c --- linux-2.4.21-bk5/fs/block_dev.c 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/fs/block_dev.c 2003-06-13 07:51:37.000000000 -0700 @@ -131,9 +131,8 @@ return 0; } -static int blkdev_direct_IO(int rw, struct file * filp, struct kiobuf * iobuf, unsigned long blocknr, int blocksize) +static int blkdev_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize) { - struct inode * inode = filp->f_dentry->d_inode->i_mapping->host; return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, blkdev_get_block); } diff -urN linux-2.4.21-bk5/fs/ext2/inode.c linux-2.4.21-bk6/fs/ext2/inode.c --- linux-2.4.21-bk5/fs/ext2/inode.c 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/fs/ext2/inode.c 2003-06-13 07:51:37.000000000 -0700 @@ -592,9 +592,8 @@ { return generic_block_bmap(mapping,block,ext2_get_block); } -static int ext2_direct_IO(int rw, struct file * filp, struct kiobuf * iobuf, unsigned long blocknr, int blocksize) +static int ext2_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize) { - struct inode * inode = filp->f_dentry->d_inode->i_mapping->host; return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, ext2_get_block); } struct address_space_operations ext2_aops = { diff -urN linux-2.4.21-bk5/fs/nfs/Makefile linux-2.4.21-bk6/fs/nfs/Makefile --- linux-2.4.21-bk5/fs/nfs/Makefile 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/fs/nfs/Makefile 2001-11-09 14:28:15.000000000 -0800 @@ -14,7 +14,6 @@ obj-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o obj-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o -obj-$(CONFIG_NFS_DIRECTIO) += direct.o obj-m := $(O_TARGET) diff -urN linux-2.4.21-bk5/fs/nfs/direct.c linux-2.4.21-bk6/fs/nfs/direct.c --- linux-2.4.21-bk5/fs/nfs/direct.c 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/fs/nfs/direct.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,382 +0,0 @@ -/* - * linux/fs/nfs/direct.c - * - * High-performance direct I/O for the NFS client - * - * When an application requests uncached I/O, all read and write requests - * are made directly to the server; data stored or fetched via these - * requests is not cached in the Linux page cache. The client does not - * correct unaligned requests from applications. All requested bytes are - * held on permanent storage before a direct write system call returns to - * an application. Applications that manage their own data caching, such - * as databases, make very good use of direct I/O on local file systems. - * - * Solaris implements an uncached I/O facility called directio() that - * is used for backups and sequential I/O to very large files. Solaris - * also supports uncaching whole NFS partitions with "-o forcedirectio," - * an undocumented mount option. - * - * Note that I/O to read in executables (e.g. kernel_read) cannot use - * direct (kiobuf) reads because there is no vma backing the passed-in - * data buffer. - * - * Designed by Jeff Kimmel, Chuck Lever, and Trond Myklebust. - * - * Initial implementation: 12/2001 by Chuck Lever - * - * TODO: - * - * 1. Use concurrent asynchronous network requests rather than - * serialized synchronous network requests for normal (non-sync) - * direct I/O. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define NFSDBG_FACILITY (NFSDBG_PAGECACHE | NFSDBG_VFS) -#define VERF_SIZE (2 * sizeof(__u32)) - -static inline int -nfs_direct_read_rpc(struct file *file, struct nfs_readargs *arg) -{ - int result; - struct inode * inode = file->f_dentry->d_inode; - struct nfs_fattr fattr; - struct rpc_message msg; - struct nfs_readres res = { &fattr, arg->count, 0 }; - -#ifdef CONFIG_NFS_V3 - msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? - NFS3PROC_READ : NFSPROC_READ; -#else - msg.rpc_proc = NFSPROC_READ; -#endif - msg.rpc_argp = arg; - msg.rpc_resp = &res; - - lock_kernel(); - msg.rpc_cred = nfs_file_cred(file); - fattr.valid = 0; - result = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - nfs_refresh_inode(inode, &fattr); - unlock_kernel(); - - return result; -} - -static inline int -nfs_direct_write_rpc(struct file *file, struct nfs_writeargs *arg, - struct nfs_writeverf *verf) -{ - int result; - struct inode *inode = file->f_dentry->d_inode; - struct nfs_fattr fattr; - struct rpc_message msg; - struct nfs_writeres res = { &fattr, verf, 0 }; - -#ifdef CONFIG_NFS_V3 - msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? - NFS3PROC_WRITE : NFSPROC_WRITE; -#else - msg.rpc_proc = NFSPROC_WRITE; -#endif - msg.rpc_argp = arg; - msg.rpc_resp = &res; - - lock_kernel(); - msg.rpc_cred = get_rpccred(nfs_file_cred(file)); - fattr.valid = 0; - result = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - nfs_write_attributes(inode, &fattr); - put_rpccred(msg.rpc_cred); - unlock_kernel(); - -#ifdef CONFIG_NFS_V3 - if (NFS_PROTO(inode)->version == 3) { - if (result > 0) { - if ((arg->stable == NFS_FILE_SYNC) && - (verf->committed != NFS_FILE_SYNC)) { - printk(KERN_ERR - "%s: server didn't sync stable write request\n", - __FUNCTION__); - return -EIO; - } - - if (result != arg->count) { - printk(KERN_INFO - "%s: short write, count=%u, result=%d\n", - __FUNCTION__, arg->count, result); - } - } - return result; - } else { -#endif - verf->committed = NFS_FILE_SYNC; /* NFSv2 always syncs data */ - if (result == 0) - return arg->count; - return result; -#ifdef CONFIG_NFS_V3 - } -#endif -} - -#ifdef CONFIG_NFS_V3 -static inline int -nfs_direct_commit_rpc(struct inode *inode, loff_t offset, size_t count, - struct nfs_writeverf *verf) -{ - int result; - struct nfs_fattr fattr; - struct nfs_writeargs arg = { NFS_FH(inode), offset, count, 0, 0, - NULL }; - struct nfs_writeres res = { &fattr, verf, 0 }; - struct rpc_message msg = { NFS3PROC_COMMIT, &arg, &res, NULL }; - - fattr.valid = 0; - - lock_kernel(); - result = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); - nfs_write_attributes(inode, &fattr); - unlock_kernel(); - - return result; -} -#else -static inline int -nfs_direct_commit_rpc(struct inode *inode, loff_t offset, size_t count, - struct nfs_writeverf *verf) -{ - return 0; -} -#endif - -/* - * Walk through the iobuf and create an iovec for each "rsize" bytes. - */ -static int -nfs_direct_read(struct file *file, struct kiobuf *iobuf, loff_t offset, - size_t count) -{ - int curpage, total; - int result = 0; - struct inode *inode = file->f_dentry->d_inode; - int rsize = NFS_SERVER(inode)->rsize; - struct page *pages[NFS_READ_MAXIOV]; - struct nfs_readargs args = { NFS_FH(inode), offset, 0, iobuf->offset, - pages }; - - total = 0; - curpage = 0; - while (count) { - int len, request; - struct page **dest = pages; - - request = count; - if (count > rsize) - request = rsize; - args.count = request; - args.offset = offset; - args.pgbase = (iobuf->offset + total) & ~PAGE_MASK; - len = PAGE_SIZE - args.pgbase; - - do { - struct page *page = iobuf->maplist[curpage]; - - if (curpage >= iobuf->nr_pages || !page) { - result = -EFAULT; - goto out_err; - } - - *dest++ = page; - /* zero after the first iov */ - if (request < len) - break; - request -= len; - len = PAGE_SIZE; - curpage++; - } while (request != 0); - - result = nfs_direct_read_rpc(file, &args); - - if (result < 0) - break; - - total += result; - if (result < args.count) /* NFSv2ism */ - break; - count -= result; - offset += result; - }; -out_err: - if (!total) - return result; - return total; -} - -/* - * Walk through the iobuf and create an iovec for each "wsize" bytes. - * If only one network write is necessary, or if the O_SYNC flag or - * 'sync' mount option are present, or if this is a V2 inode, use - * FILE_SYNC. Otherwise, use UNSTABLE and finish with a COMMIT. - * - * The mechanics of this function are much the same as nfs_direct_read, - * with the added complexity of committing unstable writes. - */ -static int -nfs_direct_write(struct file *file, struct kiobuf *iobuf, - loff_t offset, size_t count) -{ - int curpage, total; - int need_commit = 0; - int result = 0; - loff_t save_offset = offset; - struct inode *inode = file->f_dentry->d_inode; - int wsize = NFS_SERVER(inode)->wsize; - struct nfs_writeverf first_verf, ret_verf; - struct page *pages[NFS_WRITE_MAXIOV]; - struct nfs_writeargs args = { NFS_FH(inode), 0, 0, NFS_FILE_SYNC, 0, - pages }; - -#ifdef CONFIG_NFS_V3 - if ((NFS_PROTO(inode)->version == 3) && (count > wsize) && - (!IS_SYNC(inode))) - args.stable = NFS_UNSTABLE; -#endif - -retry: - total = 0; - curpage = 0; - while (count) { - int len, request; - struct page **dest = pages; - - request = count; - if (count > wsize) - request = wsize; - args.count = request; - args.offset = offset; - args.pgbase = (iobuf->offset + total) & ~PAGE_MASK; - len = PAGE_SIZE - args.pgbase; - - do { - struct page *page = iobuf->maplist[curpage]; - - if (curpage >= iobuf->nr_pages || !page) { - result = -EFAULT; - goto out_err; - } - - *dest++ = page; - /* zero after the first iov */ - if (request < len) - break; - request -= len; - len = PAGE_SIZE; - curpage++; - } while (request != 0); - - result = nfs_direct_write_rpc(file, &args, &ret_verf); - - if (result < 0) - break; - - if (!total) - memcpy(&first_verf.verifier, &ret_verf.verifier, - VERF_SIZE); - if (ret_verf.committed != NFS_FILE_SYNC) { - need_commit = 1; - if (memcmp(&first_verf.verifier, &ret_verf.verifier, - VERF_SIZE)) - goto print_retry; - } - - total += result; - count -= result; - offset += result; - }; - -out_err: - /* - * Commit data written so far, even in the event of an error - */ - if (need_commit) { - if (nfs_direct_commit_rpc(inode, save_offset, - iobuf->length - count, &ret_verf)) - goto print_retry; - if (memcmp(&first_verf.verifier, &ret_verf.verifier, - VERF_SIZE)) - goto print_retry; - } - - if (!total) - return result; - return total; - -print_retry: - printk(KERN_INFO "%s: detected server restart; retrying with FILE_SYNC\n", - __FUNCTION__); - args.stable = NFS_FILE_SYNC; - offset = save_offset; - count = iobuf->length; - goto retry; -} - -/* - * Read or write data, moving the data directly to/from the - * application's buffer without caching in the page cache. - * - * Rules for direct I/O - * - * 1. block size = 512 bytes or more - * 2. file byte offset is block aligned - * 3. byte count is a multiple of block size - * 4. user buffer is not aligned - * 5. user buffer is faulted in and pinned - * - * These are verified before we get here. - */ -int -nfs_direct_IO(int rw, struct file *file, struct kiobuf *iobuf, - unsigned long blocknr, int blocksize) -{ - int result = -EINVAL; - size_t count = iobuf->length; - struct dentry *dentry = file->f_dentry; - struct inode *inode = dentry->d_inode; - loff_t offset = blocknr << inode->i_blkbits; - - switch (rw) { - case READ: - dfprintk(VFS, - "NFS: direct_IO(READ) (%s/%s) off/cnt(%Lu/%d)\n", - dentry->d_parent->d_name.name, - dentry->d_name.name, offset, count); - - result = nfs_direct_read(file, iobuf, offset, count); - break; - case WRITE: - dfprintk(VFS, - "NFS: direct_IO(WRITE) (%s/%s) off/cnt(%Lu/%d)\n", - dentry->d_parent->d_name.name, - dentry->d_name.name, offset, count); - - result = nfs_direct_write(file, iobuf, offset, count); - break; - default: - break; - } - - dfprintk(VFS, "NFS: direct_IO result = %d\n", result); - return result; -} diff -urN linux-2.4.21-bk5/fs/nfs/file.c linux-2.4.21-bk6/fs/nfs/file.c --- linux-2.4.21-bk5/fs/nfs/file.c 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/fs/nfs/file.c 2003-06-13 07:51:37.000000000 -0700 @@ -16,7 +16,6 @@ * nfs regular file handling functions */ -#include #include #include #include @@ -201,9 +200,6 @@ sync_page: nfs_sync_page, writepage: nfs_writepage, prepare_write: nfs_prepare_write, -#ifdef CONFIG_NFS_DIRECTIO - direct_IO: nfs_direct_IO, -#endif commit_write: nfs_commit_write }; diff -urN linux-2.4.21-bk5/fs/nfs/write.c linux-2.4.21-bk6/fs/nfs/write.c --- linux-2.4.21-bk5/fs/nfs/write.c 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/fs/nfs/write.c 2003-07-10 02:55:00.000000000 -0700 @@ -123,6 +123,23 @@ } /* + * This function will be used to simulate weak cache consistency + * under NFSv2 when the NFSv3 attribute patch is included. + * For the moment, we just call nfs_refresh_inode(). + */ +static __inline__ int +nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr) +{ + if ((fattr->valid & NFS_ATTR_FATTR) && !(fattr->valid & NFS_ATTR_WCC)) { + fattr->pre_size = NFS_CACHE_ISIZE(inode); + fattr->pre_mtime = NFS_CACHE_MTIME(inode); + fattr->pre_ctime = NFS_CACHE_CTIME(inode); + fattr->valid |= NFS_ATTR_WCC; + } + return nfs_refresh_inode(inode, fattr); +} + +/* * Write a page synchronously. * Offset is the data offset within the page. */ diff -urN linux-2.4.21-bk5/fs/quota_v2.c linux-2.4.21-bk6/fs/quota_v2.c --- linux-2.4.21-bk5/fs/quota_v2.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.21-bk6/fs/quota_v2.c 2003-07-10 02:55:00.000000000 -0700 @@ -0,0 +1,690 @@ +/* + * vfsv0 quota IO operations on file + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define __QUOTA_V2_PARANOIA + +typedef char *dqbuf_t; + +#define GETIDINDEX(id, depth) (((id) >> ((V2_DQTREEDEPTH-(depth)-1)*8)) & 0xff) +#define GETENTRIES(buf) ((struct v2_disk_dqblk *)(((char *)buf)+sizeof(struct v2_disk_dqdbheader))) + +/* Check whether given file is really vfsv0 quotafile */ +static int v2_check_quota_file(struct super_block *sb, int type) +{ + struct v2_disk_dqheader dqhead; + struct file *f = sb_dqopt(sb)->files[type]; + mm_segment_t fs; + ssize_t size; + loff_t offset = 0; + static const uint quota_magics[] = V2_INITQMAGICS; + static const uint quota_versions[] = V2_INITQVERSIONS; + + fs = get_fs(); + set_fs(KERNEL_DS); + size = f->f_op->read(f, (char *)&dqhead, sizeof(struct v2_disk_dqheader), &offset); + set_fs(fs); + if (size != sizeof(struct v2_disk_dqheader)) + return 0; + if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] || + le32_to_cpu(dqhead.dqh_version) != quota_versions[type]) + return 0; + return 1; +} + +/* Read information header from quota file */ +static int v2_read_file_info(struct super_block *sb, int type) +{ + mm_segment_t fs; + struct v2_disk_dqinfo dinfo; + struct mem_dqinfo *info = sb_dqopt(sb)->info+type; + struct file *f = sb_dqopt(sb)->files[type]; + ssize_t size; + loff_t offset = V2_DQINFOOFF; + + fs = get_fs(); + set_fs(KERNEL_DS); + size = f->f_op->read(f, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), &offset); + set_fs(fs); + if (size != sizeof(struct v2_disk_dqinfo)) { + printk(KERN_WARNING "Can't read info structure on device %s.\n", + kdevname(f->f_dentry->d_sb->s_dev)); + return -1; + } + info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace); + info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace); + info->dqi_flags = le32_to_cpu(dinfo.dqi_flags); + info->u.v2_i.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks); + info->u.v2_i.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk); + info->u.v2_i.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry); + return 0; +} + +/* Write information header to quota file */ +static int v2_write_file_info(struct super_block *sb, int type) +{ + mm_segment_t fs; + struct v2_disk_dqinfo dinfo; + struct mem_dqinfo *info = sb_dqopt(sb)->info+type; + struct file *f = sb_dqopt(sb)->files[type]; + ssize_t size; + loff_t offset = V2_DQINFOOFF; + + info->dqi_flags &= ~DQF_INFO_DIRTY; + dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace); + dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace); + dinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK); + dinfo.dqi_blocks = cpu_to_le32(info->u.v2_i.dqi_blocks); + dinfo.dqi_free_blk = cpu_to_le32(info->u.v2_i.dqi_free_blk); + dinfo.dqi_free_entry = cpu_to_le32(info->u.v2_i.dqi_free_entry); + fs = get_fs(); + set_fs(KERNEL_DS); + size = f->f_op->write(f, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), &offset); + set_fs(fs); + if (size != sizeof(struct v2_disk_dqinfo)) { + printk(KERN_WARNING "Can't write info structure on device %s.\n", + kdevname(f->f_dentry->d_sb->s_dev)); + return -1; + } + return 0; +} + +static void disk2memdqb(struct mem_dqblk *m, struct v2_disk_dqblk *d) +{ + m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit); + m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit); + m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes); + m->dqb_itime = le64_to_cpu(d->dqb_itime); + m->dqb_bhardlimit = le32_to_cpu(d->dqb_bhardlimit); + m->dqb_bsoftlimit = le32_to_cpu(d->dqb_bsoftlimit); + m->dqb_curspace = le64_to_cpu(d->dqb_curspace); + m->dqb_btime = le64_to_cpu(d->dqb_btime); +} + +static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id) +{ + d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit); + d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit); + d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes); + d->dqb_itime = cpu_to_le64(m->dqb_itime); + d->dqb_bhardlimit = cpu_to_le32(m->dqb_bhardlimit); + d->dqb_bsoftlimit = cpu_to_le32(m->dqb_bsoftlimit); + d->dqb_curspace = cpu_to_le64(m->dqb_curspace); + d->dqb_btime = cpu_to_le64(m->dqb_btime); + d->dqb_id = cpu_to_le32(id); +} + +static dqbuf_t getdqbuf(void) +{ + dqbuf_t buf = kmalloc(V2_DQBLKSIZE, GFP_KERNEL); + if (!buf) + printk(KERN_WARNING "VFS: Not enough memory for quota buffers.\n"); + return buf; +} + +static inline void freedqbuf(dqbuf_t buf) +{ + kfree(buf); +} + +static ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf) +{ + mm_segment_t fs; + ssize_t ret; + loff_t offset = blk<f_op->read(filp, (char *)buf, V2_DQBLKSIZE, &offset); + set_fs(fs); + return ret; +} + +static ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf) +{ + mm_segment_t fs; + ssize_t ret; + loff_t offset = blk<f_op->write(filp, (char *)buf, V2_DQBLKSIZE, &offset); + set_fs(fs); + return ret; + +} + +/* Remove empty block from list and return it */ +static int get_free_dqblk(struct file *filp, struct mem_dqinfo *info) +{ + dqbuf_t buf = getdqbuf(); + struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; + int ret, blk; + + if (!buf) + return -ENOMEM; + if (info->u.v2_i.dqi_free_blk) { + blk = info->u.v2_i.dqi_free_blk; + if ((ret = read_blk(filp, blk, buf)) < 0) + goto out_buf; + info->u.v2_i.dqi_free_blk = le32_to_cpu(dh->dqdh_next_free); + } + else { + memset(buf, 0, V2_DQBLKSIZE); + if ((ret = write_blk(filp, info->u.v2_i.dqi_blocks, buf)) < 0) /* Assure block allocation... */ + goto out_buf; + blk = info->u.v2_i.dqi_blocks++; + } + mark_info_dirty(info); + ret = blk; +out_buf: + freedqbuf(buf); + return ret; +} + +/* Insert empty block to the list */ +static int put_free_dqblk(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk) +{ + struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; + int err; + + dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_blk); + dh->dqdh_prev_free = cpu_to_le32(0); + dh->dqdh_entries = cpu_to_le16(0); + info->u.v2_i.dqi_free_blk = blk; + mark_info_dirty(info); + if ((err = write_blk(filp, blk, buf)) < 0) /* Some strange block. We had better leave it... */ + return err; + return 0; +} + +/* Remove given block from the list of blocks with free entries */ +static int remove_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk) +{ + dqbuf_t tmpbuf = getdqbuf(); + struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; + uint nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk = le32_to_cpu(dh->dqdh_prev_free); + int err; + + if (!tmpbuf) + return -ENOMEM; + if (nextblk) { + if ((err = read_blk(filp, nextblk, tmpbuf)) < 0) + goto out_buf; + ((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free; + if ((err = write_blk(filp, nextblk, tmpbuf)) < 0) + goto out_buf; + } + if (prevblk) { + if ((err = read_blk(filp, prevblk, tmpbuf)) < 0) + goto out_buf; + ((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free; + if ((err = write_blk(filp, prevblk, tmpbuf)) < 0) + goto out_buf; + } + else { + info->u.v2_i.dqi_free_entry = nextblk; + mark_info_dirty(info); + } + freedqbuf(tmpbuf); + dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0); + if (write_blk(filp, blk, buf) < 0) /* No matter whether write succeeds block is out of list */ + printk(KERN_ERR "VFS: Can't write block (%u) with free entries.\n", blk); + return 0; +out_buf: + freedqbuf(tmpbuf); + return err; +} + +/* Insert given block to the beginning of list with free entries */ +static int insert_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk) +{ + dqbuf_t tmpbuf = getdqbuf(); + struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; + int err; + + if (!tmpbuf) + return -ENOMEM; + dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_entry); + dh->dqdh_prev_free = cpu_to_le32(0); + if ((err = write_blk(filp, blk, buf)) < 0) + goto out_buf; + if (info->u.v2_i.dqi_free_entry) { + if ((err = read_blk(filp, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0) + goto out_buf; + ((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = cpu_to_le32(blk); + if ((err = write_blk(filp, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0) + goto out_buf; + } + freedqbuf(tmpbuf); + info->u.v2_i.dqi_free_entry = blk; + mark_info_dirty(info); + return 0; +out_buf: + freedqbuf(tmpbuf); + return err; +} + +/* Find space for dquot */ +static uint find_free_dqentry(struct dquot *dquot, int *err) +{ + struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; + struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info+dquot->dq_type; + uint blk, i; + struct v2_disk_dqdbheader *dh; + struct v2_disk_dqblk *ddquot; + struct v2_disk_dqblk fakedquot; + dqbuf_t buf; + + *err = 0; + if (!(buf = getdqbuf())) { + *err = -ENOMEM; + return 0; + } + dh = (struct v2_disk_dqdbheader *)buf; + ddquot = GETENTRIES(buf); + if (info->u.v2_i.dqi_free_entry) { + blk = info->u.v2_i.dqi_free_entry; + if ((*err = read_blk(filp, blk, buf)) < 0) + goto out_buf; + } + else { + blk = get_free_dqblk(filp, info); + if ((int)blk < 0) { + *err = blk; + return 0; + } + memset(buf, 0, V2_DQBLKSIZE); + info->u.v2_i.dqi_free_entry = blk; /* This is enough as block is already zeroed and entry list is empty... */ + mark_info_dirty(info); + } + if (le16_to_cpu(dh->dqdh_entries)+1 >= V2_DQSTRINBLK) /* Block will be full? */ + if ((*err = remove_free_dqentry(filp, info, buf, blk)) < 0) { + printk(KERN_ERR "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", blk); + goto out_buf; + } + dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)+1); + memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk)); + /* Find free structure in block */ + for (i = 0; i < V2_DQSTRINBLK && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk)); i++); +#ifdef __QUOTA_V2_PARANOIA + if (i == V2_DQSTRINBLK) { + printk(KERN_ERR "VFS: find_free_dqentry(): Data block full but it shouldn't.\n"); + *err = -EIO; + goto out_buf; + } +#endif + if ((*err = write_blk(filp, blk, buf)) < 0) { + printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota data block %u.\n", blk); + goto out_buf; + } + dquot->dq_off = (blk<dq_sb)->files[dquot->dq_type]; + struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type; + dqbuf_t buf; + int ret = 0, newson = 0, newact = 0; + u32 *ref; + uint newblk; + + if (!(buf = getdqbuf())) + return -ENOMEM; + if (!*treeblk) { + ret = get_free_dqblk(filp, info); + if (ret < 0) + goto out_buf; + *treeblk = ret; + memset(buf, 0, V2_DQBLKSIZE); + newact = 1; + } + else { + if ((ret = read_blk(filp, *treeblk, buf)) < 0) { + printk(KERN_ERR "VFS: Can't read tree quota block %u.\n", *treeblk); + goto out_buf; + } + } + ref = (u32 *)buf; + newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]); + if (!newblk) + newson = 1; + if (depth == V2_DQTREEDEPTH-1) { +#ifdef __QUOTA_V2_PARANOIA + if (newblk) { + printk(KERN_ERR "VFS: Inserting already present quota entry (block %u).\n", ref[GETIDINDEX(dquot->dq_id, depth)]); + ret = -EIO; + goto out_buf; + } +#endif + newblk = find_free_dqentry(dquot, &ret); + } + else + ret = do_insert_tree(dquot, &newblk, depth+1); + if (newson && ret >= 0) { + ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(newblk); + ret = write_blk(filp, *treeblk, buf); + } + else if (newact && ret < 0) + put_free_dqblk(filp, info, buf, *treeblk); +out_buf: + freedqbuf(buf); + return ret; +} + +/* Wrapper for inserting quota structure into tree */ +static inline int dq_insert_tree(struct dquot *dquot) +{ + int tmp = V2_DQTREEOFF; + return do_insert_tree(dquot, &tmp, 0); +} + +/* + * We don't have to be afraid of deadlocks as we never have quotas on quota files... + */ +static int v2_write_dquot(struct dquot *dquot) +{ + int type = dquot->dq_type; + struct file *filp; + mm_segment_t fs; + loff_t offset; + ssize_t ret; + struct v2_disk_dqblk ddquot; + + if (!dquot->dq_off) + if ((ret = dq_insert_tree(dquot)) < 0) { + printk(KERN_ERR "VFS: Error %d occured while creating quota.\n", ret); + return ret; + } + filp = sb_dqopt(dquot->dq_sb)->files[type]; + offset = dquot->dq_off; + mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id); + fs = get_fs(); + set_fs(KERNEL_DS); + ret = filp->f_op->write(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset); + set_fs(fs); + if (ret != sizeof(struct v2_disk_dqblk)) { + printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", kdevname(dquot->dq_dev)); + if (ret >= 0) + ret = -ENOSPC; + } + else + ret = 0; + dqstats.writes++; + return ret; +} + +/* Free dquot entry in data block */ +static int free_dqentry(struct dquot *dquot, uint blk) +{ + struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; + struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type; + struct v2_disk_dqdbheader *dh; + dqbuf_t buf = getdqbuf(); + int ret = 0; + + if (!buf) + return -ENOMEM; + if (dquot->dq_off >> V2_DQBLKSIZE_BITS != blk) { + printk(KERN_ERR "VFS: Quota structure has offset to other block (%u) than it should (%u).\n", blk, (uint)(dquot->dq_off >> V2_DQBLKSIZE_BITS)); + goto out_buf; + } + if ((ret = read_blk(filp, blk, buf)) < 0) { + printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk); + goto out_buf; + } + dh = (struct v2_disk_dqdbheader *)buf; + dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)-1); + if (!le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */ + if ((ret = remove_free_dqentry(filp, info, buf, blk)) < 0 || + (ret = put_free_dqblk(filp, info, buf, blk)) < 0) { + printk(KERN_ERR "VFS: Can't move quota data block (%u) to free list.\n", blk); + goto out_buf; + } + } + else { + memset(buf+(dquot->dq_off & ((1 << V2_DQBLKSIZE_BITS)-1)), 0, sizeof(struct v2_disk_dqblk)); + if (le16_to_cpu(dh->dqdh_entries) == V2_DQSTRINBLK-1) { + /* Insert will write block itself */ + if ((ret = insert_free_dqentry(filp, info, buf, blk)) < 0) { + printk(KERN_ERR "VFS: Can't insert quota data block (%u) to free entry list.\n", blk); + goto out_buf; + } + } + else + if ((ret = write_blk(filp, blk, buf)) < 0) { + printk(KERN_ERR "VFS: Can't write quota data block %u\n", blk); + goto out_buf; + } + } + dquot->dq_off = 0; /* Quota is now unattached */ +out_buf: + freedqbuf(buf); + return ret; +} + +/* Remove reference to dquot from tree */ +static int remove_tree(struct dquot *dquot, uint *blk, int depth) +{ + struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; + struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type; + dqbuf_t buf = getdqbuf(); + int ret = 0; + uint newblk; + u32 *ref = (u32 *)buf; + + if (!buf) + return -ENOMEM; + if ((ret = read_blk(filp, *blk, buf)) < 0) { + printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk); + goto out_buf; + } + newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]); + if (depth == V2_DQTREEDEPTH-1) { + ret = free_dqentry(dquot, newblk); + newblk = 0; + } + else + ret = remove_tree(dquot, &newblk, depth+1); + if (ret >= 0 && !newblk) { + int i; + ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0); + for (i = 0; i < V2_DQBLKSIZE && !buf[i]; i++); /* Block got empty? */ + if (i == V2_DQBLKSIZE) { + put_free_dqblk(filp, info, buf, *blk); + *blk = 0; + } + else + if ((ret = write_blk(filp, *blk, buf)) < 0) + printk(KERN_ERR "VFS: Can't write quota tree block %u.\n", *blk); + } +out_buf: + freedqbuf(buf); + return ret; +} + +/* Delete dquot from tree */ +static int v2_delete_dquot(struct dquot *dquot) +{ + uint tmp = V2_DQTREEOFF; + + if (!dquot->dq_off) /* Even not allocated? */ + return 0; + return remove_tree(dquot, &tmp, 0); +} + +/* Find entry in block */ +static loff_t find_block_dqentry(struct dquot *dquot, uint blk) +{ + struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; + dqbuf_t buf = getdqbuf(); + loff_t ret = 0; + int i; + struct v2_disk_dqblk *ddquot = GETENTRIES(buf); + + if (!buf) + return -ENOMEM; + if ((ret = read_blk(filp, blk, buf)) < 0) { + printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); + goto out_buf; + } + if (dquot->dq_id) + for (i = 0; i < V2_DQSTRINBLK && le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++); + else { /* ID 0 as a bit more complicated searching... */ + struct v2_disk_dqblk fakedquot; + + memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk)); + for (i = 0; i < V2_DQSTRINBLK; i++) + if (!le32_to_cpu(ddquot[i].dqb_id) && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk))) + break; + } + if (i == V2_DQSTRINBLK) { + printk(KERN_ERR "VFS: Quota for id %u referenced but not present.\n", dquot->dq_id); + ret = -EIO; + goto out_buf; + } + else + ret = (blk << V2_DQBLKSIZE_BITS) + sizeof(struct v2_disk_dqdbheader) + i * sizeof(struct v2_disk_dqblk); +out_buf: + freedqbuf(buf); + return ret; +} + +/* Find entry for given id in the tree */ +static loff_t find_tree_dqentry(struct dquot *dquot, uint blk, int depth) +{ + struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; + dqbuf_t buf = getdqbuf(); + loff_t ret = 0; + u32 *ref = (u32 *)buf; + + if (!buf) + return -ENOMEM; + if ((ret = read_blk(filp, blk, buf)) < 0) { + printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); + goto out_buf; + } + ret = 0; + blk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]); + if (!blk) /* No reference? */ + goto out_buf; + if (depth < V2_DQTREEDEPTH-1) + ret = find_tree_dqentry(dquot, blk, depth+1); + else + ret = find_block_dqentry(dquot, blk); +out_buf: + freedqbuf(buf); + return ret; +} + +/* Find entry for given id in the tree - wrapper function */ +static inline loff_t find_dqentry(struct dquot *dquot) +{ + return find_tree_dqentry(dquot, V2_DQTREEOFF, 0); +} + +static int v2_read_dquot(struct dquot *dquot) +{ + int type = dquot->dq_type; + struct file *filp; + mm_segment_t fs; + loff_t offset; + struct v2_disk_dqblk ddquot; + int ret = 0; + + filp = sb_dqopt(dquot->dq_sb)->files[type]; + +#ifdef __QUOTA_V2_PARANOIA + if (!filp || !dquot->dq_sb) { /* Invalidated quota? */ + printk(KERN_ERR "VFS: Quota invalidated while reading!\n"); + return -EIO; + } +#endif + offset = find_dqentry(dquot); + if (offset <= 0) { /* Entry not present? */ + if (offset < 0) + printk(KERN_ERR "VFS: Can't read quota structure for id %u.\n", dquot->dq_id); + dquot->dq_off = 0; + dquot->dq_flags |= DQ_FAKE; + memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); + ret = offset; + } + else { + dquot->dq_off = offset; + fs = get_fs(); + set_fs(KERNEL_DS); + if ((ret = filp->f_op->read(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset)) != sizeof(struct v2_disk_dqblk)) { + if (ret >= 0) + ret = -EIO; + printk(KERN_ERR "VFS: Error while reading quota structure for id %u.\n", dquot->dq_id); + memset(&ddquot, 0, sizeof(struct v2_disk_dqblk)); + } + else + ret = 0; + set_fs(fs); + disk2memdqb(&dquot->dq_dqb, &ddquot); + } + dqstats.reads++; + return ret; +} + +/* Commit changes of dquot to disk - it might also mean deleting it when quota became fake one and user has no blocks... */ +static int v2_commit_dquot(struct dquot *dquot) +{ + /* We clear the flag everytime so we don't loop when there was an IO error... */ + dquot->dq_flags &= ~DQ_MOD; + if (dquot->dq_flags & DQ_FAKE && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace)) + return v2_delete_dquot(dquot); + else + return v2_write_dquot(dquot); +} + +static struct quota_format_ops v2_format_ops = { + check_quota_file: v2_check_quota_file, + read_file_info: v2_read_file_info, + write_file_info: v2_write_file_info, + free_file_info: NULL, + read_dqblk: v2_read_dquot, + commit_dqblk: v2_commit_dquot, +}; + +static struct quota_format_type v2_quota_format = { + qf_fmt_id: QFMT_VFS_V0, + qf_ops: &v2_format_ops, + qf_owner: THIS_MODULE +}; + +static int __init init_v2_quota_format(void) +{ + return register_quota_format(&v2_quota_format); +} + +static void __exit exit_v2_quota_format(void) +{ + unregister_quota_format(&v2_quota_format); +} + +EXPORT_NO_SYMBOLS; + +module_init(init_v2_quota_format); +module_exit(exit_v2_quota_format); diff -urN linux-2.4.21-bk5/include/asm-cris/atomic.h linux-2.4.21-bk6/include/asm-cris/atomic.h --- linux-2.4.21-bk5/include/asm-cris/atomic.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/atomic.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,5 +1,3 @@ -/* $Id: atomic.h,v 1.4 2002/11/20 18:20:17 hp Exp $ */ - #ifndef __ASM_CRIS_ATOMIC__ #define __ASM_CRIS_ATOMIC__ diff -urN linux-2.4.21-bk5/include/asm-cris/bitops.h linux-2.4.21-bk6/include/asm-cris/bitops.h --- linux-2.4.21-bk5/include/asm-cris/bitops.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/bitops.h 2003-07-10 02:55:00.000000000 -0700 @@ -42,6 +42,7 @@ */ #define set_bit(nr, addr) (void)test_and_set_bit(nr, addr) +#define __set_bit(nr, addr) (void)__test_and_set_bit(nr, addr) /* * clear_bit - Clears a bit in memory @@ -55,6 +56,7 @@ */ #define clear_bit(nr, addr) (void)test_and_clear_bit(nr, addr) +#define __clear_bit(nr, addr) (void)__test_and_clear_bit(nr, addr) /* * change_bit - Toggle a bit in memory @@ -105,6 +107,18 @@ return retval; } +extern inline int __test_and_set_bit(int nr, void *addr) +{ + unsigned int mask, retval; + unsigned int *adr = (unsigned int *)addr; + + adr += nr >> 5; + mask = 1 << (nr & 0x1f); + retval = (mask & *adr) != 0; + *adr |= mask; + return retval; +} + /* * clear_bit() doesn't provide any barrier for the compiler. */ diff -urN linux-2.4.21-bk5/include/asm-cris/bugs.h linux-2.4.21-bk6/include/asm-cris/bugs.h --- linux-2.4.21-bk5/include/asm-cris/bugs.h 2001-02-08 16:32:44.000000000 -0800 +++ linux-2.4.21-bk6/include/asm-cris/bugs.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,8 +1,7 @@ -/* $Id: bugs.h,v 1.2 2001/01/17 17:03:18 bjornw Exp $ - * +/* * include/asm-cris/bugs.h * - * Copyright (C) 2001 Axis Communications AB + * Copyright (C) 2001, 2002, 2003 Axis Communications AB */ /* diff -urN linux-2.4.21-bk5/include/asm-cris/byteorder.h linux-2.4.21-bk6/include/asm-cris/byteorder.h --- linux-2.4.21-bk5/include/asm-cris/byteorder.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/byteorder.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,5 +1,3 @@ -/* $Id: byteorder.h,v 1.2 2002/11/20 18:20:17 hp Exp $ */ - #ifndef _CRIS_BYTEORDER_H #define _CRIS_BYTEORDER_H @@ -15,14 +13,14 @@ extern __inline__ __const__ __u32 ___arch__swab32(__u32 x) { __asm__ ("swapwb %0" : "=r" (x) : "0" (x)); - + return(x); } extern __inline__ __const__ __u16 ___arch__swab16(__u16 x) { __asm__ ("swapb %0" : "=r" (x) : "0" (x)); - + return(x); } @@ -43,5 +41,3 @@ #include #endif - - diff -urN linux-2.4.21-bk5/include/asm-cris/delay.h linux-2.4.21-bk6/include/asm-cris/delay.h --- linux-2.4.21-bk5/include/asm-cris/delay.h 2001-10-08 11:43:54.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/delay.h 2003-07-10 02:55:00.000000000 -0700 @@ -39,6 +39,13 @@ __delay(usecs * loops_per_usec); } +/* ETRAX 100 is really too slow to sleep for nanosecs. */ +/* Divide with 1024 to avoid a real division that would take >1 us... */ +extern __inline__ void ndelay(unsigned long nsecs) +{ + __delay(1 + nsecs * loops_per_usec / 1024); +} + #endif /* defined(_CRIS_DELAY_H) */ diff -urN linux-2.4.21-bk5/include/asm-cris/dma.h linux-2.4.21-bk6/include/asm-cris/dma.h --- linux-2.4.21-bk5/include/asm-cris/dma.h 2001-07-04 11:50:39.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/dma.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: dma.h,v 1.2 2001/05/09 12:17:42 johana Exp $ +/* * linux/include/asm/dma.h: Defines for using and allocating dma channels. */ diff -urN linux-2.4.21-bk5/include/asm-cris/eshlibld.h linux-2.4.21-bk6/include/asm-cris/eshlibld.h --- linux-2.4.21-bk5/include/asm-cris/eshlibld.h 2001-04-06 10:51:19.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/eshlibld.h 2003-07-10 02:55:00.000000000 -0700 @@ -9,10 +9,9 @@ *! *!--------------------------------------------------------------------------- *! -*! (C) Copyright 1998, 1999 Axis Communications AB, LUND, SWEDEN +*! (C) Copyright 1998-2003 Axis Communications AB, LUND, SWEDEN *! *!**************************************************************************/ -/* $Id: eshlibld.h,v 1.2 2001/02/23 13:47:33 bjornw Exp $ */ #ifndef _cris_relocate_h #define _cris_relocate_h @@ -111,4 +110,3 @@ #endif _cris_relocate_h /********************** END OF FILE eshlibld.h *****************************/ - diff -urN linux-2.4.21-bk5/include/asm-cris/etraxgpio.h linux-2.4.21-bk6/include/asm-cris/etraxgpio.h --- linux-2.4.21-bk5/include/asm-cris/etraxgpio.h 2002-11-28 15:53:15.000000000 -0800 +++ linux-2.4.21-bk6/include/asm-cris/etraxgpio.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,4 +1,3 @@ -/* $Id: etraxgpio.h,v 1.8 2002/06/17 15:53:07 johana Exp $ */ /* * The following devices are accessable using this driver using * GPIO_MAJOR (120) and a couple of minor numbers: @@ -6,11 +5,12 @@ * /dev/gpiob minor 1, 8 bit GPIO, each bit can change direction * /dev/leds minor 2, Access to leds depending on kernelconfig * /dev/gpiog minor 3 - g0dir, g8_15dir, g16_23dir, g24 dir configurable in R_GEN_CONFIG - g1-g7 and g25-g31 is both input and outputs but on different pins - Also note that some bits change pins depending on what interfaces - are enabled. + * g0dir, g8_15dir, g16_23dir, g24 dir configurable in R_GEN_CONFIG + * g1-g7 and g25-g31 is both input and outputs but on different pins + * Also note that some bits change pins depending on what interfaces + * are enabled. */ + #ifndef _ASM_ETRAXGPIO_H #define _ASM_ETRAXGPIO_H @@ -41,7 +41,7 @@ /* GPIO direction ioctl's */ #define IO_READDIR 0x8 /* Read direction 0=input 1=output (obsolete) */ -#define IO_SETINPUT 0x9 /* Set direction for bits set, 0=unchanged 1=input, +#define IO_SETINPUT 0x9 /* Set direction for bits set, 0=unchanged 1=input, returns mask with current inputs (obsolete) */ #define IO_SETOUTPUT 0xA /* Set direction for bits set, 0=unchanged 1=output, returns mask with current outputs (obsolete)*/ @@ -55,11 +55,11 @@ #define IO_GET_PWR_BT 0xE /* Bit toggling in driver settings */ -/* bit set in low byte0 is CLK mask (0x00FF), - bit set in byte1 is DATA mask (0xFF00) +/* bit set in low byte0 is CLK mask (0x00FF), + bit set in byte1 is DATA mask (0xFF00) msb, data_mask[7:0] , clk_mask[7:0] */ -#define IO_CFG_WRITE_MODE 0xF +#define IO_CFG_WRITE_MODE 0xF #define IO_CFG_WRITE_MODE_VALUE(msb, data_mask, clk_mask) \ ( (((msb)&1) << 16) | (((data_mask) &0xFF) << 8) | ((clk_mask) & 0xFF) ) @@ -76,6 +76,4 @@ * *arg updated with current output pins. */ - - #endif diff -urN linux-2.4.21-bk5/include/asm-cris/etraxi2c.h linux-2.4.21-bk6/include/asm-cris/etraxi2c.h --- linux-2.4.21-bk5/include/asm-cris/etraxi2c.h 2001-04-06 10:51:19.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/etraxi2c.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,5 +1,3 @@ -/* $Id: etraxi2c.h,v 1.1 2001/01/18 15:49:57 bjornw Exp $ */ - #ifndef _LINUX_ETRAXI2C_H #define _LINUX_ETRAXI2C_H diff -urN linux-2.4.21-bk5/include/asm-cris/etraxvirtex.h linux-2.4.21-bk6/include/asm-cris/etraxvirtex.h --- linux-2.4.21-bk5/include/asm-cris/etraxvirtex.h 2002-11-28 15:53:15.000000000 -0800 +++ linux-2.4.21-bk6/include/asm-cris/etraxvirtex.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,5 +1,3 @@ -/* $Id: etraxvirtex.h,v 1.1 2002/06/25 10:01:05 stefanl Exp $ */ - #ifndef _LINUX_ETRAXVIRTEX_FPGA_H #define _LINUX_ETRAXVIRTEX_FPGA_H diff -urN linux-2.4.21-bk5/include/asm-cris/fasttimer.h linux-2.4.21-bk6/include/asm-cris/fasttimer.h --- linux-2.4.21-bk5/include/asm-cris/fasttimer.h 2002-11-28 15:53:15.000000000 -0800 +++ linux-2.4.21-bk6/include/asm-cris/fasttimer.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,16 +1,17 @@ -/* $Id: fasttimer.h,v 1.2 2002/05/28 17:45:34 johana Exp $ +/* * linux/include/asm-cris/fasttimer.h * * Fast timers for ETRAX100LX * This may be useful in other OS than Linux so use 2 space indentation... - * Copyright (C) 2000, 2002 Axis Communications AB + * Copyright (C) 2000, 2001, 2002, 2003 Axis Communications AB */ + #include #include /* struct timeval */ #include -/* The timer0 values gives 52us resolution (1/19200) or higher - * but interrupts at HZ +/* The timer0 values gives 52us resolution (1/19200) or higher + * but interrupts at HZ */ /* We use timer1 to generate interrupts at desired times. */ @@ -18,7 +19,7 @@ typedef void fast_timer_function_type(unsigned long); -struct fast_timer{ /* Close to timer_list */ +struct fast_timer { /* Close to timer_list */ struct fast_timer *next; struct fast_timer *prev; struct timeval tv_set; @@ -29,6 +30,8 @@ const char *name; }; +extern struct fast_timer *fast_timer_list; + void start_one_shot_timer(struct fast_timer *t, fast_timer_function_type *function, unsigned long data, @@ -38,6 +41,10 @@ int del_fast_timer(struct fast_timer * t); /* return 1 if deleted */ +extern inline int fast_timer_pending (const struct fast_timer * t) +{ + return (t->next != NULL) || (t->prev != NULL) || (t == fast_timer_list); +} void schedule_usleep(unsigned long us); diff -urN linux-2.4.21-bk5/include/asm-cris/ide.h linux-2.4.21-bk6/include/asm-cris/ide.h --- linux-2.4.21-bk5/include/asm-cris/ide.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/ide.h 2003-07-10 02:55:00.000000000 -0700 @@ -17,6 +17,9 @@ #ifdef __KERNEL__ #include +#include +#include + /* ETRAX 100 can support 4 IDE busses on the same pins (serialized) */ diff -urN linux-2.4.21-bk5/include/asm-cris/io.h linux-2.4.21-bk6/include/asm-cris/io.h --- linux-2.4.21-bk5/include/asm-cris/io.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/io.h 2003-07-10 02:55:00.000000000 -0700 @@ -258,6 +258,8 @@ #define IO_SPACE_LIMIT 0xffff #define inb(x) (0) +#define inw(x) (0) +#define inl(x) (0) #define outb(x,y) #define outw(x,y) #define outl(x,y) diff -urN linux-2.4.21-bk5/include/asm-cris/irq.h linux-2.4.21-bk6/include/asm-cris/irq.h --- linux-2.4.21-bk5/include/asm-cris/irq.h 2002-02-25 11:38:11.000000000 -0800 +++ linux-2.4.21-bk6/include/asm-cris/irq.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,7 +1,7 @@ /* * Interrupt handling assembler and defines for Linux/CRIS * - * Copyright (c) 2000, 2001 Axis Communications AB + * Copyright (c) 2000, 2001, 2002, 2003 Axis Communications AB * */ @@ -18,60 +18,60 @@ #include #define NR_IRQS 32 -#define SOME_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, some) /* 0 ? */ -#define NMI_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, nmi) /* 1 */ -#define TIMER0_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, timer0) /* 2 */ -#define TIMER1_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, timer1) /* 3 */ +#define SOME_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, some) /* 0 ? */ +#define NMI_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, nmi) /* 1 */ +#define TIMER0_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, timer0) /* 2 */ +#define TIMER1_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, timer1) /* 3 */ /* mio, ata, par0, scsi0 on 4 */ /* par1, scsi1 on 5 */ #define NETWORK_STATUS_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, network) /* 6 */ -#define SERIAL_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, serial) /* 8 */ -#define PA_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, pa) /* 11 */ +#define SERIAL_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, serial) /* 8 */ +#define PA_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, pa) /* 11 */ /* extdma0 and extdma1 is at irq 12 and 13 and/or same as dma5 and dma6 ? */ -#define EXTDMA0_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, ext_dma0) -#define EXTDMA1_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, ext_dma1) +#define EXTDMA0_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, ext_dma0) +#define EXTDMA1_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, ext_dma1) + +#define BUS_FAULT_IRQ_NBR 14 +#define MULTIPLE_IRQ_NBR 15 /* dma0-9 is irq 16..25 */ /* 16,17: network */ -#define DMA0_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma0) -#define DMA1_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma1) +#define DMA0_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma0) +#define DMA1_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma1) #define NETWORK_DMA_TX_IRQ_NBR DMA0_TX_IRQ_NBR #define NETWORK_DMA_RX_IRQ_NBR DMA1_RX_IRQ_NBR /* 18,19: dma2 and dma3 shared by par0, scsi0, ser2 and ata */ -#define DMA2_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma2) -#define DMA3_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma3) -#define SER2_DMA_TX_IRQ_NBR DMA2_TX_IRQ_NBR -#define SER2_DMA_RX_IRQ_NBR DMA3_RX_IRQ_NBR +#define DMA2_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma2) +#define DMA3_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma3) +#define SER2_DMA_TX_IRQ_NBR DMA2_TX_IRQ_NBR +#define SER2_DMA_RX_IRQ_NBR DMA3_RX_IRQ_NBR /* 20,21: dma4 and dma5 shared by par1, scsi1, ser3 and extdma0 */ -#define DMA4_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma4) -#define DMA5_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma5) -#define SER3_DMA_TX_IRQ_NBR DMA4_TX_IRQ_NBR -#define SER3_DMA_RX_IRQ_NBR DMA5_RX_IRQ_NBR +#define DMA4_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma4) +#define DMA5_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma5) +#define SER3_DMA_TX_IRQ_NBR DMA4_TX_IRQ_NBR +#define SER3_DMA_RX_IRQ_NBR DMA5_RX_IRQ_NBR /* 22,23: dma6 and dma7 shared by ser0, extdma1 and mem2mem */ -#define DMA6_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma6) -#define DMA7_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma7) -#define SER0_DMA_TX_IRQ_NBR DMA6_TX_IRQ_NBR -#define SER0_DMA_RX_IRQ_NBR DMA7_RX_IRQ_NBR +#define DMA6_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma6) +#define DMA7_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma7) +#define SER0_DMA_TX_IRQ_NBR DMA6_TX_IRQ_NBR +#define SER0_DMA_RX_IRQ_NBR DMA7_RX_IRQ_NBR #define MEM2MEM_DMA_TX_IRQ_NBR DMA6_TX_IRQ_NBR #define MEM2MEM_DMA_RX_IRQ_NBR DMA7_RX_IRQ_NBR /* 24,25: dma8 and dma9 shared by ser1 and usb */ -#define DMA8_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma8) -#define DMA9_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma9) -#define SER1_DMA_TX_IRQ_NBR DMA8_TX_IRQ_NBR -#define SER1_DMA_RX_IRQ_NBR DMA9_RX_IRQ_NBR -#define USB_DMA_TX_IRQ_NBR DMA8_TX_IRQ_NBR -#define USB_DMA_RX_IRQ_NBR DMA9_RX_IRQ_NBR +#define DMA8_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma8) +#define DMA9_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma9) +#define SER1_DMA_TX_IRQ_NBR DMA8_TX_IRQ_NBR +#define SER1_DMA_RX_IRQ_NBR DMA9_RX_IRQ_NBR +#define USB_DMA_TX_IRQ_NBR DMA8_TX_IRQ_NBR +#define USB_DMA_RX_IRQ_NBR DMA9_RX_IRQ_NBR /* usb: controller at irq 31 + uses DMA8 and DMA9 */ -#define USB_HC_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, usb) - - - +#define USB_HC_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, usb) extern void disable_irq(unsigned int); @@ -94,7 +94,7 @@ #define __STR(x) #x #define STR(x) __STR(x) - + /* SAVE_ALL saves registers so they match pt_regs */ #define SAVE_ALL \ @@ -102,34 +102,34 @@ "push $srp\n\t" /* push subroutine return pointer */ \ "push $dccr\n\t" /* push condition codes */ \ "push $mof\n\t" /* push multiply overflow reg */ \ - "di\n\t" /* need to disable irq's at this point */\ + "di\n\t" /* need to disable irq's at this point */\ "subq 14*4,$sp\n\t" /* make room for r0-r13 */ \ "movem $r13,[$sp]\n\t" /* push the r0-r13 registers */ \ "push $r10\n\t" /* push orig_r10 */ \ "clear.d [$sp=$sp-4]\n\t" /* frametype - this is a normal stackframe */ - /* BLOCK_IRQ and UNBLOCK_IRQ do the same as mask_irq and unmask_irq in irq.c */ +/* BLOCK_IRQ and UNBLOCK_IRQ do the same as mask_irq and unmask_irq in irq.c */ #define BLOCK_IRQ(mask,nr) \ "move.d " #mask ",$r0\n\t" \ - "move.d $r0,[0xb00000d8]\n\t" - + "move.d $r0,[0xb00000d8]\n\t" + #define UNBLOCK_IRQ(mask) \ "move.d " #mask ",$r0\n\t" \ - "move.d $r0,[0xb00000dc]\n\t" + "move.d $r0,[0xb00000dc]\n\t" #define IRQ_NAME2(nr) nr##_interrupt(void) #define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr) #define sIRQ_NAME(nr) IRQ_NAME2(sIRQ##nr) #define BAD_IRQ_NAME(nr) IRQ_NAME2(bad_IRQ##nr) - /* the asm IRQ handler makes sure the causing IRQ is blocked, then it calls - * do_IRQ (with irq disabled still). after that it unblocks and jumps to - * ret_from_intr (entry.S) - * - * The reason the IRQ is blocked is to allow an sti() before the handler which - * will acknowledge the interrupt is run. - */ +/* the asm IRQ handler makes sure the causing IRQ is blocked, then it calls + * do_IRQ (with irq disabled still). after that it unblocks and jumps to + * ret_from_intr (entry.S) + * + * The reason the IRQ is blocked is to allow an sti() before the handler which + * will acknowledge the interrupt is run. + */ #define BUILD_IRQ(nr,mask) \ void IRQ_NAME(nr); \ @@ -154,19 +154,20 @@ "reti\n\t" \ "nop\n"); -/* This is subtle. The timer interrupt is crucial and it should not be disabled for - * too long. However, if it had been a normal interrupt as per BUILD_IRQ, it would - * have been BLOCK'ed, and then softirq's are run before we return here to UNBLOCK. - * If the softirq's take too much time to run, the timer irq won't run and the - * watchdog will kill us. +/* This is subtle. The timer interrupt is crucial and it should not be disabled + * for too long. However, if it had been a normal interrupt as per BUILD_IRQ, + * it would have been BLOCK'ed, and then softirq's are run before we return + * here to UNBLOCK. If the softirq's take too much time to run, the timer irq + * won't run and the watchdog will kill us. * - * Furthermore, if a lot of other irq's occur before we return here, the multiple_irq - * handler is run and it prioritizes the timer interrupt. However if we had BLOCK'ed - * it here, we would not get the multiple_irq at all. + * Furthermore, if a lot of other irq's occur before we return here, the + * multiple_irq handler is run and it prioritizes the timer interrupt. However + * if we had BLOCK'ed it here, we would not get the multiple_irq at all. * - * The non-blocking here is based on the knowledge that the timer interrupt is - * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will not - * be an sti() before the timer irq handler is run to acknowledge the interrupt. + * The non-blocking here is based on the knowledge that the timer interrupt is + * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will + * not be an sti() before the timer irq handler is run to acknowledge the + * interrupt. */ #define BUILD_TIMER_IRQ(nr,mask) \ @@ -191,5 +192,3 @@ "nop\n"); #endif /* _ASM_IRQ_H */ - - diff -urN linux-2.4.21-bk5/include/asm-cris/namei.h linux-2.4.21-bk6/include/asm-cris/namei.h --- linux-2.4.21-bk5/include/asm-cris/namei.h 2001-02-08 16:32:44.000000000 -0800 +++ linux-2.4.21-bk6/include/asm-cris/namei.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: namei.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $ +/* * linux/include/asm-cris/namei.h * * Included from linux/fs/namei.c @@ -9,7 +9,7 @@ /* used to find file-system prefixes for doing emulations * see for example asm-sparc/namei.h - * we dont use it... + * we don't use it... */ #define __emul_prefix() NULL diff -urN linux-2.4.21-bk5/include/asm-cris/pci.h linux-2.4.21-bk6/include/asm-cris/pci.h --- linux-2.4.21-bk5/include/asm-cris/pci.h 2001-02-08 16:32:44.000000000 -0800 +++ linux-2.4.21-bk6/include/asm-cris/pci.h 2003-07-10 02:55:00.000000000 -0700 @@ -5,5 +5,6 @@ * includes it even if CONFIG_PCI is not set. */ +#define PCI_DMA_BUS_IS_PHYS (0) #endif /* __ASM_CRIS_PCI_H */ diff -urN linux-2.4.21-bk5/include/asm-cris/posix_types.h linux-2.4.21-bk6/include/asm-cris/posix_types.h --- linux-2.4.21-bk5/include/asm-cris/posix_types.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/posix_types.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,5 +1,3 @@ -/* $Id: posix_types.h,v 1.2 2002/09/26 14:44:59 hp Exp $ */ - /* We cheat a bit and use our C-coded bitops functions from asm/bitops.h */ /* I guess we should write these in assembler because they are used often. */ diff -urN linux-2.4.21-bk5/include/asm-cris/processor.h linux-2.4.21-bk6/include/asm-cris/processor.h --- linux-2.4.21-bk5/include/asm-cris/processor.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/processor.h 2003-07-10 02:55:00.000000000 -0700 @@ -65,7 +65,7 @@ /* * At user->kernel entry, the pt_regs struct is stacked on the top of the kernel-stack. * This macro allows us to find those regs for a task. - * Notice that subsequent pt_regs stackings, like recursive interrupts occuring while + * Notice that subsequent pt_regs stackings, like recursive interrupts occurring while * we're in the kernel, won't affect this - only the first user->kernel transition * registers are reached by this. */ diff -urN linux-2.4.21-bk5/include/asm-cris/rs485.h linux-2.4.21-bk6/include/asm-cris/rs485.h --- linux-2.4.21-bk5/include/asm-cris/rs485.h 2002-08-02 17:39:45.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/rs485.h 2003-07-10 02:55:00.000000000 -0700 @@ -5,7 +5,7 @@ struct rs485_control { unsigned short rts_on_send; unsigned short rts_after_sent; - __u32 delay_rts_before_send; + unsigned long delay_rts_before_send; unsigned short enabled; #ifdef __KERNEL__ int disable_serial_loopback; @@ -14,7 +14,7 @@ /* Used with ioctl() TIOCSERWRRS485 */ struct rs485_write { - __u16 outc_size; + unsigned short outc_size; unsigned char *outc; }; diff -urN linux-2.4.21-bk5/include/asm-cris/rtc.h linux-2.4.21-bk6/include/asm-cris/rtc.h --- linux-2.4.21-bk5/include/asm-cris/rtc.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/rtc.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,9 +1,6 @@ -/* $Id: rtc.h,v 1.8 2002/11/20 14:27:25 oskarp Exp $ */ - #ifndef __RTC_H__ #define __RTC_H__ - #include #ifdef CONFIG_ETRAX_DS1302 @@ -20,13 +17,13 @@ /* Bits in CONTROL register. */ # define RTC_CONTROL_WRITEPROTECT 0x80 # define RTC_TRICKLECHARGER 8 - + /* Bits in TRICKLECHARGER register TCS TCS TCS TCS DS DS RS RS. */ # define RTC_TCR_PATTERN 0xA0 /* 1010xxxx */ # define RTC_TCR_1DIOD 0x04 /* xxxx01xx */ # define RTC_TCR_2DIOD 0x08 /* xxxx10xx */ # define RTC_TCR_DISABLED 0x00 /* xxxxxx00 Disabled */ -# define RTC_TCR_2KOHM 0x01 /* xxxxxx01 2KOhm */ +# define RTC_TCR_2KOHM 0x01 /* xxxxxx01 2kOhm */ # define RTC_TCR_4KOHM 0x02 /* xxxxxx10 4kOhm */ # define RTC_TCR_8KOHM 0x03 /* xxxxxx11 8kOhm */ @@ -36,11 +33,11 @@ # define RTC_I2C_WRITE 0xa2 /* Phillips PCF8563 registers. */ -# define RTC_CONTROL1 0x00 /* Control/Status register 1. */ -# define RTC_CONTROL2 0x01 /* Control/Status register 2. */ -# define RTC_CLOCKOUT_FREQ 0x0d /* CLKOUT frequency. */ -# define RTC_TIMER_CONTROL 0x0e /* Timer control. */ -# define RTC_TIMER_CNTDOWN 0x0f /* Timer countdown. */ +# define RTC_CONTROL1 0x00 /* Control/Status register 1. */ +# define RTC_CONTROL2 0x01 /* Control/Status register 2. */ +# define RTC_CLOCKOUT_FREQ 0x0d /* CLKOUT frequency. */ +# define RTC_TIMER_CONTROL 0x0e /* Timer control. */ +# define RTC_TIMER_CNTDOWN 0x0f /* Timer countdown. */ /* BCD encoded clock registers. */ # define RTC_SECONDS 0x02 @@ -53,7 +50,26 @@ # define RTC_MINUTE_ALARM 0x09 # define RTC_HOUR_ALARM 0x0a # define RTC_DAY_ALARM 0x0b -# define RTC_WEEKDAY_ALARM 0x0c /* Not coded in BCD! */ +# define RTC_WEEKDAY_ALARM 0x0c /* Not coded in BCD! */ + +/* Control/Status 2 bits */ +# define RTC_CTRL2_TIE 0x01 /* Timer Interrupt Enable */ +# define RTC_CTRL2_AIE 0x02 /* Alarm Interrupt Enable */ +# define RTC_CTRL2_TF 0x04 /* Timer Flag */ +# define RTC_CTRL2_AF 0x08 /* Alarm Flag */ +# define RTC_CTRL2_TP 0x10 /* Timer Pulse */ + +/* Timer Control bits */ +# define RTC_TC_TD0 0x01 +# define RTC_TC_TD1 0x02 +# define RTC_TC_TE 0x80 /* Timer Enable */ + +/* Timer source clock frequency */ +# define RTC_TC_FREQ_MASK (RTC_TC_TD0 | RTC_TC_TD1) +# define RTC_TC_FREQ_4096 (0) +# define RTC_TC_FREQ_64 (RTC_TC_TD0) +# define RTC_TC_FREQ_1 (RTC_TC_TD1) +# define RTC_TC_FREQ_1_60 (RTC_TC_TD0 | RTC_TC_TD1) #endif @@ -84,7 +100,7 @@ /* * The struct used to pass data via the following ioctl. Similar to the - * struct tm in , but it needs to be here so that the kernel + * struct tm in , but it needs to be here so that the kernel * source is self contained, allowing cross-compiles, etc. etc. */ struct rtc_time { @@ -101,9 +117,11 @@ /* ioctl() calls that are permitted to the /dev/rtc interface. */ #define RTC_MAGIC 'p' -#define RTC_RD_TIME _IOR(RTC_MAGIC, 0x09, struct rtc_time) /* Read RTC time. */ -#define RTC_SET_TIME _IOW(RTC_MAGIC, 0x0a, struct rtc_time) /* Set RTC time. */ -#define RTC_SET_CHARGE _IOW(RTC_MAGIC, 0x0b, int) -#define RTC_MAX_IOCTL 0x0b +#define RTC_RD_TIME _IOR(RTC_MAGIC, 0x09, struct rtc_time) /* Read RTC time. */ +#define RTC_SET_TIME _IOW(RTC_MAGIC, 0x0a, struct rtc_time) /* Set RTC time. */ +#define RTC_SET_CHARGE _IOW(RTC_MAGIC, 0x0b, int) +#define RTC_VLOW_RD _IOR(RTC_MAGIC, 0x11, int) /* Voltage Low detector */ +#define RTC_VLOW_SET _IO(RTC_MAGIC, 0x12) /* Clear voltage low information */ +#define RTC_MAX_IOCTL 0x12 #endif /* __RTC_H__ */ diff -urN linux-2.4.21-bk5/include/asm-cris/semaphore-helper.h linux-2.4.21-bk6/include/asm-cris/semaphore-helper.h --- linux-2.4.21-bk5/include/asm-cris/semaphore-helper.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/semaphore-helper.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,7 +1,6 @@ -/* $Id: semaphore-helper.h,v 1.4 2002/11/20 18:20:17 hp Exp $ - * +/* * SMP- and interrupt-safe semaphores helper functions. Generic versions, no - * optimizations whatsoever... + * optimizations whatsoever... * */ @@ -73,5 +72,3 @@ } #endif /* _ASM_SEMAPHORE_HELPER_H */ - - diff -urN linux-2.4.21-bk5/include/asm-cris/semaphore.h linux-2.4.21-bk6/include/asm-cris/semaphore.h --- linux-2.4.21-bk5/include/asm-cris/semaphore.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/semaphore.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,5 +1,3 @@ -/* $Id: semaphore.h,v 1.5 2002/12/02 08:14:08 starvik Exp $ */ - /* On the i386 these are coded in asm, perhaps we should as well. Later.. */ #ifndef _CRIS_SEMAPHORE_H @@ -15,7 +13,7 @@ #include /* - * CRIS semaphores, implemented in C-only so far. + * CRIS semaphores, implemented in C-only so far. */ int printk(const char *fmt, ...); @@ -140,7 +138,7 @@ * jumps for both down() and up(). */ extern inline void up(struct semaphore * sem) -{ +{ unsigned long flags; int wakeup; diff -urN linux-2.4.21-bk5/include/asm-cris/sigcontext.h linux-2.4.21-bk6/include/asm-cris/sigcontext.h --- linux-2.4.21-bk5/include/asm-cris/sigcontext.h 2001-02-08 16:32:44.000000000 -0800 +++ linux-2.4.21-bk6/include/asm-cris/sigcontext.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,16 +1,14 @@ -/* $Id: sigcontext.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $ */ - #ifndef _ASM_CRIS_SIGCONTEXT_H #define _ASM_CRIS_SIGCONTEXT_H #include -/* This struct is saved by setup_frame in signal.c, to keep the current context while - a signal handler is executed. It's restored by sys_sigreturn. - - To keep things simple, we use pt_regs here even though normally you just specify - the list of regs to save. Then we can use copy_from_user on the entire regs instead - of a bunch of get_user's as well... +/* This struct is saved by setup_frame in signal.c, to keep the current context + while a signal handler is executed. It's restored by sys_sigreturn. + + To keep things simple, we use pt_regs here even though normally you just + specify the list of regs to save. Then we can use copy_from_user on the + entire regs instead of a bunch of get_user's as well... */ @@ -21,4 +19,3 @@ }; #endif - diff -urN linux-2.4.21-bk5/include/asm-cris/system.h linux-2.4.21-bk6/include/asm-cris/system.h --- linux-2.4.21-bk5/include/asm-cris/system.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/system.h 2003-07-10 02:55:00.000000000 -0700 @@ -14,6 +14,15 @@ #define switch_to(prev,next,last) last = resume(prev,next, \ (int)&((struct task_struct *)0)->thread) +/* read the CPU PC register */ + +extern inline unsigned long rdpc(void) +{ + unsigned long pc; + __asm__ volatile ("move.d $pc,%0" : "=rm" (pc)); + return pc; +} + /* read the CPU version register */ extern inline unsigned long rdvr(void) { @@ -54,6 +63,7 @@ struct __xchg_dummy { unsigned long a[100]; }; #define __xg(x) ((struct __xchg_dummy *)(x)) +#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT #if 0 /* use these and an oscilloscope to see the fraction of time we're running with IRQ's disabled */ /* it assumes the LED's are on port 0x90000000 of course. */ @@ -62,6 +72,60 @@ #define save_flags(x) __asm__ __volatile__ ("move $ccr,%0" : "=rm" (x) : : "memory"); #define restore_flags(x) __asm__ __volatile__ ("move %0,$ccr\n\tbtstq 5,%0\n\tbpl 1f\n\tnop\n\tpush $r0\n\tmoveq 0,$r0\n\tmove.d $r0,[0x90000000]\n\tpop $r0\n1:\n" : : "r" (x) : "memory"); #else + +/* Log when interrupts are turned on and off and who did it. */ +#define CCR_EI_MASK (1 << 5) +/* in debug.c */ +extern int log_int_pos; +extern int log_int_size; +extern int log_int_enable; +extern int log_int_trig0_pos; +extern int log_int_trig1_pos; +extern void log_int(unsigned long pc, unsigned long prev_ccr, unsigned long next_ccr); + +/* If you only want to log changes - change to 1 to a 0 below */ +#define LOG_INT(pc, curr_ccr, next_ccr) do { \ + if (1 || (curr_ccr ^ next_ccr) & CCR_EI_MASK) \ + log_int((pc), curr_ccr, next_ccr); \ + }while(0) + +#define __save_flags(x) __asm__ __volatile__ ("move $ccr,%0" : "=rm" (x) : : "memory"); + +extern inline void __cli(void) +{ + unsigned long pc = rdpc(); + unsigned long curr_ccr; __save_flags(curr_ccr); + LOG_INT(pc, curr_ccr, 0); + __asm__ __volatile__ ( "di" : : :"memory"); +} + + +extern inline void __sti(void) +{ + unsigned long pc = rdpc(); + unsigned long curr_ccr; __save_flags(curr_ccr); + LOG_INT(pc, curr_ccr, CCR_EI_MASK); + __asm__ __volatile__ ( "ei" : : :"memory"); +} + +extern inline void __restore_flags(unsigned long x) +{ + unsigned long pc = rdpc(); + unsigned long curr_ccr; __save_flags(curr_ccr); + LOG_INT(pc, curr_ccr, x); + __asm__ __volatile__ ("move %0,$ccr" : : "rm" (x) : "memory"); +} + +/* For spinlocks etc */ +#define local_irq_save(x) do { __save_flags(x); __cli(); }while (0) +#define local_irq_restore(x) restore_flags(x) + +#define local_irq_disable() cli() +#define local_irq_enable() sti() + +#endif + +#else #define __cli() __asm__ __volatile__ ( "di" : : :"memory"); #define __sti() __asm__ __volatile__ ( "ei" : : :"memory"); #define __save_flags(x) __asm__ __volatile__ ("move $ccr,%0" : "=rm" (x) : : "memory"); diff -urN linux-2.4.21-bk5/include/asm-cris/termbits.h linux-2.4.21-bk6/include/asm-cris/termbits.h --- linux-2.4.21-bk5/include/asm-cris/termbits.h 2001-02-08 16:32:44.000000000 -0800 +++ linux-2.4.21-bk6/include/asm-cris/termbits.h 2003-07-10 02:55:00.000000000 -0700 @@ -1,5 +1,3 @@ -/* $Id: termbits.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $ */ - #ifndef __ARCH_ETRAX100_TERMBITS_H__ #define __ARCH_ETRAX100_TERMBITS_H__ @@ -128,6 +126,7 @@ #define B1843200 0010006 #define B6250000 0010007 #define CIBAUD 002003600000 /* input baud rate (not used) */ +#define CMSPAR 010000000000 /* mark or space (stick) parity, PARODD => mark parity */ #define CRTSCTS 020000000000 /* flow control */ /* c_lflag bits */ diff -urN linux-2.4.21-bk5/include/asm-cris/timex.h linux-2.4.21-bk6/include/asm-cris/timex.h --- linux-2.4.21-bk5/include/asm-cris/timex.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/timex.h 2003-07-10 02:55:00.000000000 -0700 @@ -26,6 +26,8 @@ unsigned long get_ns_in_jiffie(void); +unsigned long timer_data_to_ns(unsigned long timer_data); + extern inline unsigned long get_us_in_jiffie_highres(void) { return get_ns_in_jiffie()/1000; diff -urN linux-2.4.21-bk5/include/asm-cris/uaccess.h linux-2.4.21-bk6/include/asm-cris/uaccess.h --- linux-2.4.21-bk5/include/asm-cris/uaccess.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-cris/uaccess.h 2003-07-10 02:55:00.000000000 -0700 @@ -3,6 +3,15 @@ * Hans-Peter Nilsson (hp@axis.com) * * $Log: uaccess.h,v $ + * Revision 1.12 2003/06/17 14:00:42 starvik + * Merge of Linux 2.4.21 + * + * Revision 1.11 2003/06/04 19:36:45 hp + * Remove unused copy-pasted register clobber from __asm_clear + * + * Revision 1.10 2003/04/09 08:22:38 pkj + * Typo correction (taken from Linux 2.5). + * * Revision 1.9 2002/11/20 18:20:17 hp * Make all static inline functions extern inline. * @@ -129,7 +138,7 @@ * This gets kind of ugly. We want to return _two_ values in "get_user()" * and yet we don't want to do any pointers, because that is too much * of a performance impact. Thus we have a few rather ugly macros here, - * and hide all the uglyness from the user. + * and hide all the ugliness from the user. * * The "__xxx" versions of the user access functions are versions that * do not verify the address space, that must have been done previously @@ -775,7 +784,7 @@ " .previous" \ : "=r" (to), "=r" (ret) \ : "0" (to), "1" (ret) \ - : "r9", "memory") + : "memory") #define __asm_clear_1(to, ret) \ __asm_clear(to, ret, \ diff -urN linux-2.4.21-bk5/include/asm-i386/acpi.h linux-2.4.21-bk6/include/asm-i386/acpi.h --- linux-2.4.21-bk5/include/asm-i386/acpi.h 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-i386/acpi.h 2003-07-10 02:55:00.000000000 -0700 @@ -165,6 +165,9 @@ /* early initialization routine */ extern void acpi_reserve_bootmem(void); +/* Check for special HW using OEM name lists */ +extern void acpi_madt_oem_check(char *oem_id, char *oem_table_id); + #endif /*CONFIG_ACPI_SLEEP*/ diff -urN linux-2.4.21-bk5/include/asm-ppc/posix_types.h linux-2.4.21-bk6/include/asm-ppc/posix_types.h --- linux-2.4.21-bk5/include/asm-ppc/posix_types.h 2003-06-13 07:51:38.000000000 -0700 +++ linux-2.4.21-bk6/include/asm-ppc/posix_types.h 2003-07-10 02:55:00.000000000 -0700 @@ -8,7 +8,7 @@ */ typedef unsigned int __kernel_dev_t; -typedef unsigned int __kernel_ino_t; +typedef unsigned long __kernel_ino_t; typedef unsigned int __kernel_mode_t; typedef unsigned short __kernel_nlink_t; typedef long __kernel_off_t; diff -urN linux-2.4.21-bk5/include/asm-sparc/ipc.h linux-2.4.21-bk6/include/asm-sparc/ipc.h --- linux-2.4.21-bk5/include/asm-sparc/ipc.h 1999-12-20 22:05:52.000000000 -0800 +++ linux-2.4.21-bk6/include/asm-sparc/ipc.h 2003-07-10 02:55:00.000000000 -0700 @@ -14,6 +14,7 @@ #define SEMOP 1 #define SEMGET 2 #define SEMCTL 3 +#define SEMTIMEDOP 4 #define MSGSND 11 #define MSGRCV 12 #define MSGGET 13 diff -urN linux-2.4.21-bk5/include/asm-sparc64/ipc.h linux-2.4.21-bk6/include/asm-sparc64/ipc.h --- linux-2.4.21-bk5/include/asm-sparc64/ipc.h 1999-12-20 22:05:52.000000000 -0800 +++ linux-2.4.21-bk6/include/asm-sparc64/ipc.h 2003-07-10 02:55:00.000000000 -0700 @@ -14,6 +14,7 @@ #define SEMOP 1 #define SEMGET 2 #define SEMCTL 3 +#define SEMTIMEDOP 4 #define MSGSND 11 #define MSGRCV 12 #define MSGGET 13 diff -urN linux-2.4.21-bk5/include/linux/fs.h linux-2.4.21-bk6/include/linux/fs.h --- linux-2.4.21-bk5/include/linux/fs.h 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/include/linux/fs.h 2003-07-10 02:55:00.000000000 -0700 @@ -395,7 +395,7 @@ int (*flushpage) (struct page *, unsigned long); int (*releasepage) (struct page *, int); #define KERNEL_HAS_O_DIRECT /* this is for modules out of the kernel */ - int (*direct_IO)(int, struct file *, struct kiobuf *, unsigned long, int); + int (*direct_IO)(int, struct inode *, struct kiobuf *, unsigned long, int); void (*removepage)(struct page *); /* called when page gets removed from the inode */ }; @@ -1537,6 +1537,9 @@ extern int inode_change_ok(struct inode *, struct iattr *); extern int inode_setattr(struct inode *, struct iattr *); +/* kernel/fork.c */ +extern int unshare_files(void); + /* * Common dentry functions for inclusion in the VFS * or in other stackable file systems. Some of these diff -urN linux-2.4.21-bk5/include/linux/nfs_fs.h linux-2.4.21-bk6/include/linux/nfs_fs.h --- linux-2.4.21-bk5/include/linux/nfs_fs.h 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/include/linux/nfs_fs.h 2003-06-13 07:51:38.000000000 -0700 @@ -274,11 +274,6 @@ #define NFS_TestClearPageSync(page) test_and_clear_bit(PG_fs_1, &(page)->flags) /* - * linux/fs/nfs/direct.c - */ -extern int nfs_direct_IO(int, struct file *, struct kiobuf *, unsigned long, int); - -/* * linux/fs/mount_clnt.c * (Used only by nfsroot module) */ @@ -307,23 +302,6 @@ return __nfs_refresh_inode(inode,fattr); } -/* - * This function will be used to simulate weak cache consistency - * under NFSv2 when the NFSv3 attribute patch is included. - * For the moment, we just call nfs_refresh_inode(). - */ -static __inline__ int -nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr) -{ - if ((fattr->valid & NFS_ATTR_FATTR) && !(fattr->valid & NFS_ATTR_WCC)) { - fattr->pre_size = NFS_CACHE_ISIZE(inode); - fattr->pre_mtime = NFS_CACHE_MTIME(inode); - fattr->pre_ctime = NFS_CACHE_CTIME(inode); - fattr->valid |= NFS_ATTR_WCC; - } - return nfs_refresh_inode(inode, fattr); -} - static inline loff_t nfs_size_to_loff_t(__u64 size) { diff -urN linux-2.4.21-bk5/include/linux/nfs_xdr.h linux-2.4.21-bk6/include/linux/nfs_xdr.h --- linux-2.4.21-bk5/include/linux/nfs_xdr.h 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/include/linux/nfs_xdr.h 2002-11-28 15:53:15.000000000 -0800 @@ -59,7 +59,7 @@ /* Arguments to the read call. * Note that NFS_READ_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h */ -#define NFS_READ_MAXIOV (9) +#define NFS_READ_MAXIOV 8 struct nfs_readargs { struct nfs_fh * fh; @@ -78,7 +78,7 @@ /* Arguments to the write call. * Note that NFS_WRITE_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h */ -#define NFS_WRITE_MAXIOV (9) +#define NFS_WRITE_MAXIOV 8 struct nfs_writeargs { struct nfs_fh * fh; __u64 offset; diff -urN linux-2.4.21-bk5/include/net/pkt_sched.h linux-2.4.21-bk6/include/net/pkt_sched.h --- linux-2.4.21-bk5/include/net/pkt_sched.h 2002-08-02 17:39:46.000000000 -0700 +++ linux-2.4.21-bk6/include/net/pkt_sched.h 2003-07-10 02:55:00.000000000 -0700 @@ -59,7 +59,7 @@ int (*enqueue)(struct sk_buff *, struct Qdisc *); struct sk_buff * (*dequeue)(struct Qdisc *); int (*requeue)(struct sk_buff *, struct Qdisc *); - int (*drop)(struct Qdisc *); + unsigned int (*drop)(struct Qdisc *); int (*init)(struct Qdisc *, struct rtattr *arg); void (*reset)(struct Qdisc *); diff -urN linux-2.4.21-bk5/kernel/ksyms.c linux-2.4.21-bk6/kernel/ksyms.c --- linux-2.4.21-bk5/kernel/ksyms.c 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/kernel/ksyms.c 2003-07-10 02:55:00.000000000 -0700 @@ -590,6 +590,7 @@ EXPORT_SYMBOL(tasklist_lock); EXPORT_SYMBOL(pidhash); +EXPORT_SYMBOL(unshare_files); /* debug */ EXPORT_SYMBOL(dump_stack); diff -urN linux-2.4.21-bk5/mm/filemap.c linux-2.4.21-bk6/mm/filemap.c --- linux-2.4.21-bk5/mm/filemap.c 2003-07-10 02:54:56.000000000 -0700 +++ linux-2.4.21-bk6/mm/filemap.c 2003-07-10 02:55:00.000000000 -0700 @@ -1621,7 +1621,7 @@ if (retval) break; - retval = mapping->a_ops->direct_IO(rw, filp, iobuf, (offset+progress) >> blocksize_bits, blocksize); + retval = mapping->a_ops->direct_IO(rw, inode, iobuf, (offset+progress) >> blocksize_bits, blocksize); if (rw == READ && retval > 0) mark_dirty_kiobuf(iobuf, retval); diff -urN linux-2.4.21-bk5/mm/oom_kill.c linux-2.4.21-bk6/mm/oom_kill.c --- linux-2.4.21-bk5/mm/oom_kill.c 2002-11-28 15:53:15.000000000 -0800 +++ linux-2.4.21-bk6/mm/oom_kill.c 2003-07-10 02:55:00.000000000 -0700 @@ -61,6 +61,10 @@ if (!p->mm) return 0; + + if (p->flags & PF_MEMDIE) + return 0; + /* * The memory size of the process is the basis for the badness. */ diff -urN linux-2.4.21-bk5/net/ipv4/ipconfig.c linux-2.4.21-bk6/net/ipv4/ipconfig.c --- linux-2.4.21-bk5/net/ipv4/ipconfig.c 2002-11-28 15:53:15.000000000 -0800 +++ linux-2.4.21-bk6/net/ipv4/ipconfig.c 2003-07-10 02:55:00.000000000 -0700 @@ -1114,6 +1114,10 @@ "nameserver %u.%u.%u.%u\n", NIPQUAD(ic_nameservers[i])); } + if (ic_servaddr != INADDR_NONE) + len += sprintf(buffer + len, + "bootserver %u.%u.%u.%u\n", + NIPQUAD(ic_servaddr)); if (offset > len) offset = len; diff -urN linux-2.4.21-bk5/net/netsyms.c linux-2.4.21-bk6/net/netsyms.c --- linux-2.4.21-bk5/net/netsyms.c 2003-07-10 02:54:57.000000000 -0700 +++ linux-2.4.21-bk6/net/netsyms.c 2003-07-10 02:55:00.000000000 -0700 @@ -560,6 +560,7 @@ #ifdef CONFIG_NET_SCHED PSCHED_EXPORTLIST; EXPORT_SYMBOL(pfifo_qdisc_ops); +EXPORT_SYMBOL(bfifo_qdisc_ops); EXPORT_SYMBOL(register_qdisc); EXPORT_SYMBOL(unregister_qdisc); EXPORT_SYMBOL(qdisc_get_rtab); diff -urN linux-2.4.21-bk5/net/packet/af_packet.c linux-2.4.21-bk6/net/packet/af_packet.c --- linux-2.4.21-bk5/net/packet/af_packet.c 2002-08-02 17:39:46.000000000 -0700 +++ linux-2.4.21-bk6/net/packet/af_packet.c 2003-07-10 02:55:00.000000000 -0700 @@ -1378,8 +1378,13 @@ po = sk->protinfo.af_packet; switch (msg) { - case NETDEV_DOWN: case NETDEV_UNREGISTER: +#ifdef CONFIG_PACKET_MULTICAST + if (po->mclist) + packet_dev_mclist(dev, po->mclist, -1); + // fallthrough +#endif + case NETDEV_DOWN: if (dev->ifindex == po->ifindex) { spin_lock(&po->bind_lock); if (po->running) { @@ -1396,10 +1401,6 @@ } spin_unlock(&po->bind_lock); } -#ifdef CONFIG_PACKET_MULTICAST - if (po->mclist) - packet_dev_mclist(dev, po->mclist, -1); -#endif break; case NETDEV_UP: spin_lock(&po->bind_lock); @@ -1409,10 +1410,6 @@ po->running = 1; } spin_unlock(&po->bind_lock); -#ifdef CONFIG_PACKET_MULTICAST - if (po->mclist) - packet_dev_mclist(dev, po->mclist, +1); -#endif break; } } diff -urN linux-2.4.21-bk5/net/sched/sch_atm.c linux-2.4.21-bk6/net/sched/sch_atm.c --- linux-2.4.21-bk5/net/sched/sch_atm.c 2003-07-10 02:54:57.000000000 -0700 +++ linux-2.4.21-bk6/net/sched/sch_atm.c 2003-07-10 02:55:00.000000000 -0700 @@ -547,15 +547,16 @@ } -static int atm_tc_drop(struct Qdisc *sch) +static unsigned int atm_tc_drop(struct Qdisc *sch) { struct atm_qdisc_data *p = PRIV(sch); struct atm_flow_data *flow; + unsigned int len; DPRINTK("atm_tc_drop(sch %p,[qdisc %p])\n",sch,p); for (flow = p->flows; flow; flow = flow->next) - if (flow->q->ops->drop && flow->q->ops->drop(flow->q)) - return 1; + if (flow->q->ops->drop && (len = flow->q->ops->drop(flow->q))) + return len; return 0; } diff -urN linux-2.4.21-bk5/net/sched/sch_cbq.c linux-2.4.21-bk6/net/sched/sch_cbq.c --- linux-2.4.21-bk5/net/sched/sch_cbq.c 2003-07-10 02:54:57.000000000 -0700 +++ linux-2.4.21-bk6/net/sched/sch_cbq.c 2003-07-10 02:55:00.000000000 -0700 @@ -1231,11 +1231,12 @@ } } -static int cbq_drop(struct Qdisc* sch) +static unsigned int cbq_drop(struct Qdisc* sch) { struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data; struct cbq_class *cl, *cl_head; int prio; + unsigned int len; for (prio = TC_CBQ_MAXPRIO; prio >= 0; prio--) { if ((cl_head = q->active[prio]) == NULL) @@ -1243,9 +1244,9 @@ cl = cl_head; do { - if (cl->q->ops->drop && cl->q->ops->drop(cl->q)) { + if (cl->q->ops->drop && (len = cl->q->ops->drop(cl->q))) { sch->q.qlen--; - return 1; + return len; } } while ((cl = cl->next_alive) != cl_head); } diff -urN linux-2.4.21-bk5/net/sched/sch_dsmark.c linux-2.4.21-bk6/net/sched/sch_dsmark.c --- linux-2.4.21-bk5/net/sched/sch_dsmark.c 2001-12-21 09:42:06.000000000 -0800 +++ linux-2.4.21-bk6/net/sched/sch_dsmark.c 2003-07-10 02:55:00.000000000 -0700 @@ -301,17 +301,18 @@ } -static int dsmark_drop(struct Qdisc *sch) +static unsigned int dsmark_drop(struct Qdisc *sch) { struct dsmark_qdisc_data *p = PRIV(sch); - + unsigned int len; + DPRINTK("dsmark_reset(sch %p,[qdisc %p])\n",sch,p); if (!p->q->ops->drop) return 0; - if (!p->q->ops->drop(p->q)) + if (!(len = p->q->ops->drop(p->q))) return 0; sch->q.qlen--; - return 1; + return len; } diff -urN linux-2.4.21-bk5/net/sched/sch_fifo.c linux-2.4.21-bk6/net/sched/sch_fifo.c --- linux-2.4.21-bk5/net/sched/sch_fifo.c 2001-12-21 09:42:06.000000000 -0800 +++ linux-2.4.21-bk6/net/sched/sch_fifo.c 2003-07-10 02:55:00.000000000 -0700 @@ -80,16 +80,17 @@ return skb; } -static int +static unsigned int fifo_drop(struct Qdisc* sch) { struct sk_buff *skb; skb = __skb_dequeue_tail(&sch->q); if (skb) { - sch->stats.backlog -= skb->len; + unsigned int len = skb->len; + sch->stats.backlog -= len; kfree_skb(skb); - return 1; + return len; } return 0; } diff -urN linux-2.4.21-bk5/net/sched/sch_gred.c linux-2.4.21-bk6/net/sched/sch_gred.c --- linux-2.4.21-bk5/net/sched/sch_gred.c 2003-06-13 07:51:39.000000000 -0700 +++ linux-2.4.21-bk6/net/sched/sch_gred.c 2003-07-10 02:55:00.000000000 -0700 @@ -259,8 +259,7 @@ return NULL; } -static int -gred_drop(struct Qdisc* sch) +static unsigned int gred_drop(struct Qdisc* sch) { struct sk_buff *skb; @@ -269,20 +268,21 @@ skb = __skb_dequeue_tail(&sch->q); if (skb) { - sch->stats.backlog -= skb->len; + unsigned int len = skb->len; + sch->stats.backlog -= len; sch->stats.drops++; q= t->tab[(skb->tc_index&0xf)]; if (q) { - q->backlog -= skb->len; + q->backlog -= len; q->other++; if (!q->backlog && !t->eqp) PSCHED_GET_TIME(q->qidlestart); - } else { - D2PRINTK("gred_dequeue: skb has bad tcindex %x\n",skb->tc_index&0xf); - } + } else { + D2PRINTK("gred_dequeue: skb has bad tcindex %x\n",skb->tc_index&0xf); + } kfree_skb(skb); - return 1; + return len; } q=t->tab[t->def]; diff -urN linux-2.4.21-bk5/net/sched/sch_htb.c linux-2.4.21-bk6/net/sched/sch_htb.c --- linux-2.4.21-bk5/net/sched/sch_htb.c 2003-07-10 02:54:57.000000000 -0700 +++ linux-2.4.21-bk6/net/sched/sch_htb.c 2003-07-10 02:55:00.000000000 -0700 @@ -1070,7 +1070,7 @@ } /* try to drop from each class (by prio) until one succeed */ -static int htb_drop(struct Qdisc* sch) +static unsigned int htb_drop(struct Qdisc* sch) { struct htb_sched *q = (struct htb_sched *)sch->data; int prio; @@ -1078,14 +1078,15 @@ for (prio = TC_HTB_NUMPRIO - 1; prio >= 0; prio--) { struct list_head *p; list_for_each (p,q->drops+prio) { - struct htb_class *cl = list_entry(p,struct htb_class, - un.leaf.drop_list); + struct htb_class *cl = list_entry(p, struct htb_class, + un.leaf.drop_list); + unsigned int len; if (cl->un.leaf.q->ops->drop && - cl->un.leaf.q->ops->drop(cl->un.leaf.q)) { + (len = cl->un.leaf.q->ops->drop(cl->un.leaf.q))) { sch->q.qlen--; if (!cl->un.leaf.q->q.qlen) htb_deactivate (q,cl); - return 1; + return len; } } } diff -urN linux-2.4.21-bk5/net/sched/sch_ingress.c linux-2.4.21-bk6/net/sched/sch_ingress.c --- linux-2.4.21-bk5/net/sched/sch_ingress.c 2003-07-10 02:54:57.000000000 -0700 +++ linux-2.4.21-bk6/net/sched/sch_ingress.c 2003-07-10 02:55:00.000000000 -0700 @@ -190,7 +190,7 @@ return 0; } -static int ingress_drop(struct Qdisc *sch) +static unsigned int ingress_drop(struct Qdisc *sch) { #ifdef DEBUG_INGRESS struct ingress_qdisc_data *p = PRIV(sch); diff -urN linux-2.4.21-bk5/net/sched/sch_prio.c linux-2.4.21-bk6/net/sched/sch_prio.c --- linux-2.4.21-bk5/net/sched/sch_prio.c 2003-06-13 07:51:39.000000000 -0700 +++ linux-2.4.21-bk6/net/sched/sch_prio.c 2003-07-10 02:55:00.000000000 -0700 @@ -124,18 +124,18 @@ } -static int -prio_drop(struct Qdisc* sch) +static unsigned int prio_drop(struct Qdisc* sch) { struct prio_sched_data *q = (struct prio_sched_data *)sch->data; int prio; + unsigned int len; struct Qdisc *qdisc; for (prio = q->bands-1; prio >= 0; prio--) { qdisc = q->queues[prio]; - if (qdisc->ops->drop(qdisc)) { + if ((len = qdisc->ops->drop(qdisc)) != 0) { sch->q.qlen--; - return 1; + return len; } } return 0; diff -urN linux-2.4.21-bk5/net/sched/sch_red.c linux-2.4.21-bk6/net/sched/sch_red.c --- linux-2.4.21-bk5/net/sched/sch_red.c 2001-12-21 09:42:06.000000000 -0800 +++ linux-2.4.21-bk6/net/sched/sch_red.c 2003-07-10 02:55:00.000000000 -0700 @@ -342,19 +342,19 @@ return NULL; } -static int -red_drop(struct Qdisc* sch) +static unsigned int red_drop(struct Qdisc* sch) { struct sk_buff *skb; struct red_sched_data *q = (struct red_sched_data *)sch->data; skb = __skb_dequeue_tail(&sch->q); if (skb) { - sch->stats.backlog -= skb->len; + unsigned int len = skb->len; + sch->stats.backlog -= len; sch->stats.drops++; q->st.other++; kfree_skb(skb); - return 1; + return len; } PSCHED_GET_TIME(q->qidlestart); return 0; diff -urN linux-2.4.21-bk5/net/sched/sch_sfq.c linux-2.4.21-bk6/net/sched/sch_sfq.c --- linux-2.4.21-bk5/net/sched/sch_sfq.c 2003-06-13 07:51:39.000000000 -0700 +++ linux-2.4.21-bk6/net/sched/sch_sfq.c 2003-07-10 02:55:00.000000000 -0700 @@ -209,11 +209,12 @@ sfq_link(q, x); } -static int sfq_drop(struct Qdisc *sch) +static unsigned int sfq_drop(struct Qdisc *sch) { struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data; sfq_index d = q->max_depth; struct sk_buff *skb; + unsigned int len; /* Queue is full! Find the longest slot and drop a packet from it */ @@ -221,12 +222,13 @@ if (d > 1) { sfq_index x = q->dep[d+SFQ_DEPTH].next; skb = q->qs[x].prev; + len = skb->len; __skb_unlink(skb, &q->qs[x]); kfree_skb(skb); sfq_dec(q, x); sch->q.qlen--; sch->stats.drops++; - return 1; + return len; } if (d == 1) { @@ -235,13 +237,14 @@ q->next[q->tail] = q->next[d]; q->allot[q->next[d]] += q->quantum; skb = q->qs[d].prev; + len = skb->len; __skb_unlink(skb, &q->qs[d]); kfree_skb(skb); sfq_dec(q, d); sch->q.qlen--; q->ht[q->hash[d]] = SFQ_DEPTH; sch->stats.drops++; - return 1; + return len; } return 0; diff -urN linux-2.4.21-bk5/net/sched/sch_tbf.c linux-2.4.21-bk6/net/sched/sch_tbf.c --- linux-2.4.21-bk5/net/sched/sch_tbf.c 2001-12-21 09:42:06.000000000 -0800 +++ linux-2.4.21-bk6/net/sched/sch_tbf.c 2003-07-10 02:55:00.000000000 -0700 @@ -7,6 +7,8 @@ * 2 of the License, or (at your option) any later version. * * Authors: Alexey Kuznetsov, + * Dmitry Torokhov - allow attaching inner qdiscs - + * original idea by Martin Devera * */ @@ -123,62 +125,63 @@ long ptokens; /* Current number of P tokens */ psched_time_t t_c; /* Time check-point */ struct timer_list wd_timer; /* Watchdog timer */ + struct Qdisc *qdisc; /* Inner qdisc, default - bfifo queue */ }; #define L2T(q,L) ((q)->R_tab->data[(L)>>(q)->R_tab->rate.cell_log]) #define L2T_P(q,L) ((q)->P_tab->data[(L)>>(q)->P_tab->rate.cell_log]) -static int -tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch) +static int tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch) { struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + int ret; - if (skb->len > q->max_size) - goto drop; - __skb_queue_tail(&sch->q, skb); - if ((sch->stats.backlog += skb->len) <= q->limit) { - sch->stats.bytes += skb->len; - sch->stats.packets++; - return 0; - } - - /* Drop action: undo the things that we just did, - * i.e. make tail drop - */ - - __skb_unlink(skb, &sch->q); - sch->stats.backlog -= skb->len; - -drop: - sch->stats.drops++; + if (skb->len > q->max_size || sch->stats.backlog + skb->len > q->limit) { + sch->stats.drops++; #ifdef CONFIG_NET_CLS_POLICE - if (sch->reshape_fail==NULL || sch->reshape_fail(skb, sch)) + if (sch->reshape_fail == NULL || sch->reshape_fail(skb, sch)) #endif - kfree_skb(skb); - return NET_XMIT_DROP; -} - -static int -tbf_requeue(struct sk_buff *skb, struct Qdisc* sch) -{ - __skb_queue_head(&sch->q, skb); + kfree_skb(skb); + + return NET_XMIT_DROP; + } + + if ((ret = q->qdisc->enqueue(skb, q->qdisc)) != 0) { + sch->stats.drops++; + return ret; + } + + sch->q.qlen++; sch->stats.backlog += skb->len; + sch->stats.bytes += skb->len; + sch->stats.packets++; return 0; } -static int -tbf_drop(struct Qdisc* sch) +static int tbf_requeue(struct sk_buff *skb, struct Qdisc* sch) { - struct sk_buff *skb; + struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + int ret; + + if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0) { + sch->q.qlen++; + sch->stats.backlog += skb->len; + } + + return ret; +} - skb = __skb_dequeue_tail(&sch->q); - if (skb) { - sch->stats.backlog -= skb->len; +static unsigned int tbf_drop(struct Qdisc* sch) +{ + struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + unsigned int len; + + if ((len = q->qdisc->ops->drop(q->qdisc)) != 0) { + sch->q.qlen--; + sch->stats.backlog -= len; sch->stats.drops++; - kfree_skb(skb); - return 1; } - return 0; + return len; } static void tbf_watchdog(unsigned long arg) @@ -189,19 +192,19 @@ netif_schedule(sch->dev); } -static struct sk_buff * -tbf_dequeue(struct Qdisc* sch) +static struct sk_buff *tbf_dequeue(struct Qdisc* sch) { struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; struct sk_buff *skb; - skb = __skb_dequeue(&sch->q); + skb = q->qdisc->dequeue(q->qdisc); if (skb) { psched_time_t now; long toks; long ptoks = 0; - + unsigned int len = skb->len; + PSCHED_GET_TIME(now); toks = PSCHED_TDIFF_SAFE(now, q->t_c, q->buffer, 0); @@ -210,18 +213,19 @@ ptoks = toks + q->ptokens; if (ptoks > (long)q->mtu) ptoks = q->mtu; - ptoks -= L2T_P(q, skb->len); + ptoks -= L2T_P(q, len); } toks += q->tokens; if (toks > (long)q->buffer) toks = q->buffer; - toks -= L2T(q, skb->len); + toks -= L2T(q, len); if ((toks|ptoks) >= 0) { q->t_c = now; q->tokens = toks; q->ptokens = ptoks; - sch->stats.backlog -= skb->len; + sch->stats.backlog -= len; + sch->q.qlen--; sch->flags &= ~TCQ_F_THROTTLED; return skb; } @@ -245,20 +249,25 @@ This is the main idea of all FQ algorithms (cf. CSZ, HPFQ, HFSC) */ - __skb_queue_head(&sch->q, skb); - + + if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) { + /* When requeue fails skb is dropped */ + sch->q.qlen--; + sch->stats.backlog -= len; + sch->stats.drops++; + } + sch->flags |= TCQ_F_THROTTLED; sch->stats.overlimits++; } return NULL; } - -static void -tbf_reset(struct Qdisc* sch) +static void tbf_reset(struct Qdisc* sch) { struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + qdisc_reset(q->qdisc); skb_queue_purge(&sch->q); sch->stats.backlog = 0; PSCHED_GET_TIME(q->t_c); @@ -268,6 +277,31 @@ del_timer(&q->wd_timer); } +static struct Qdisc *tbf_create_dflt_qdisc(struct net_device *dev, u32 limit) +{ + struct Qdisc *q = qdisc_create_dflt(dev, &bfifo_qdisc_ops); + struct rtattr *rta; + int ret; + + if (q) { + rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL); + if (rta) { + rta->rta_type = RTM_NEWQDISC; + rta->rta_len = RTA_LENGTH(sizeof(struct tc_fifo_qopt)); + ((struct tc_fifo_qopt *)RTA_DATA(rta))->limit = limit; + + ret = q->ops->change(q, rta); + kfree(rta); + + if (ret == 0) + return q; + } + qdisc_destroy(q); + } + + return NULL; +} + static int tbf_change(struct Qdisc* sch, struct rtattr *opt) { int err = -EINVAL; @@ -276,6 +310,7 @@ struct tc_tbf_qopt *qopt; struct qdisc_rate_table *rtab = NULL; struct qdisc_rate_table *ptab = NULL; + struct Qdisc *child = NULL; int max_size,n; if (rtattr_parse(tb, TCA_TBF_PTAB, RTA_DATA(opt), RTA_PAYLOAD(opt)) || @@ -308,8 +343,14 @@ } if (max_size < 0) goto done; + + if (q->qdisc == &noop_qdisc) { + if ((child = tbf_create_dflt_qdisc(sch->dev, qopt->limit)) == NULL) + goto done; + } sch_tree_lock(sch); + if (child) q->qdisc = child; q->limit = qopt->limit; q->mtu = qopt->mtu; q->max_size = max_size; @@ -342,6 +383,8 @@ init_timer(&q->wd_timer); q->wd_timer.function = tbf_watchdog; q->wd_timer.data = (unsigned long)sch; + + q->qdisc = &noop_qdisc; if ((err = tbf_change(sch, opt)) != 0) { MOD_DEC_USE_COUNT; @@ -359,6 +402,9 @@ qdisc_put_rtab(q->P_tab); if (q->R_tab) qdisc_put_rtab(q->R_tab); + + qdisc_destroy(q->qdisc); + q->qdisc = &noop_qdisc; MOD_DEC_USE_COUNT; } @@ -391,10 +437,93 @@ return -1; } +static int tbf_dump_class(struct Qdisc *sch, unsigned long cl, + struct sk_buff *skb, struct tcmsg *tcm) +{ + struct tbf_sched_data *q = (struct tbf_sched_data*)sch->data; + + if (cl != 1) /* only one class */ + return -ENOENT; + + tcm->tcm_parent = TC_H_ROOT; + tcm->tcm_handle = 1; + tcm->tcm_info = q->qdisc->handle; + + return 0; +} + +static int tbf_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, + struct Qdisc **old) +{ + struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + + if (new == NULL) + new = &noop_qdisc; + + sch_tree_lock(sch); + *old = xchg(&q->qdisc, new); + qdisc_reset(*old); + sch_tree_unlock(sch); + + return 0; +} + +static struct Qdisc *tbf_leaf(struct Qdisc *sch, unsigned long arg) +{ + struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + return q->qdisc; +} + +static unsigned long tbf_get(struct Qdisc *sch, u32 classid) +{ + return 1; +} + +static void tbf_put(struct Qdisc *sch, unsigned long arg) +{ +} + +static int tbf_change_class(struct Qdisc *sch, u32 classid, u32 parentid, + struct rtattr **tca, unsigned long *arg) +{ + return -ENOSYS; +} + +static int tbf_delete(struct Qdisc *sch, unsigned long arg) +{ + return -ENOSYS; +} + +static void tbf_walk(struct Qdisc *sch, struct qdisc_walker *walker) +{ + struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data; + + if (!walker->stop) { + if (walker->count >= walker->skip) + if (walker->fn(sch, (unsigned long)q, walker) < 0) { + walker->stop = 1; + return; + } + walker->count++; + } +} + +static struct Qdisc_class_ops tbf_class_ops = +{ + .graft = tbf_graft, + .leaf = tbf_leaf, + .get = tbf_get, + .put = tbf_put, + .change = tbf_change_class, + .delete = tbf_delete, + .walk = tbf_walk, + .dump = tbf_dump_class, +}; + struct Qdisc_ops tbf_qdisc_ops = { NULL, - NULL, + &tbf_class_ops, "tbf", sizeof(struct tbf_sched_data),