diff options
| author | Mikael Starvik <mikael.starvik@axis.com> | 2003-07-09 20:30:06 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.osdl.org> | 2003-07-09 20:30:06 -0700 |
| commit | 3d10ee29052913cb98b0fdef2b8e3225eac89170 (patch) | |
| tree | 40441df68ada0229de5ca6696b7673758edc6158 /arch | |
| parent | 495c3da118c8b380933cee1dc2a77a8f334ad579 (diff) | |
[PATCH] CRIS architecture update for 2.5.74
This brings the chris architecture up to date with 2.5.
The patch is huge because files have been moved to prepare
for future subarchitechtures.
Diffstat (limited to 'arch')
94 files changed, 8999 insertions, 11921 deletions
diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index b092fb53d4cb..05fb1fe7620f 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig @@ -42,9 +42,9 @@ config ETRAX_KGDB this option is turned on! config ETRAX_WATCHDOG - bool "Enable Etrax100 watchdog" + bool "Enable ETRAX watchdog" help - Enable the built-in watchdog timer support on Etrax100 embedded + Enable the built-in watchdog timer support on ETRAX based embedded network computers. config ETRAX_WATCHDOG_NICE_DOGGY @@ -55,6 +55,26 @@ config ETRAX_WATCHDOG_NICE_DOGGY printing oopses. Recommended for development systems but not for production releases. +config ETRAX_FAST_TIMER + bool "Enable ETRAX fast timer API" + help + This options enables the API to a fast timer implementation using + timer1 to get sub jiffie resolution timers (primarily one-shot + timers). + This is needed if CONFIG_ETRAX_SERIAL_FAST_TIMER is enabled. + +config PREEMPT + bool "Preemptible Kernel" + help + This option reduces the latency of the kernel when reacting to + real-time or interactive events by allowing a low priority process to + be preempted even if it is in kernel mode executing a system call. + This allows applications to run more reliably even when the system is + under load. + + Say Y here if you are building a kernel for a desktop, embedded + or real-time system. Say N if you are unsure. + endmenu @@ -65,32 +85,36 @@ choice default ETRAX100LX config ETRAX100LX - bool "Etrax-100-LX-v1" + bool "ETRAX-100LX-v1" help - Support version 1 of the Etrax 100LX. + Support version 1 of the ETRAX 100LX. config ETRAX100LX_V2 - bool "Etrax-100-LX-v2" + bool "ETRAX-100LX-v2" help - Support version 2 of the Etrax 100LX. + Support version 2 of the ETRAX 100LX. config SVINTO_SIM - bool "Etrax-100-LX-for-xsim-simulator" + bool "ETRAX-100LX-for-xsim-simulator" help Support the xsim ETRAX Simulator. +config ETRAX200LX + bool "ETRAX-200LX-V32" + help + Support CRIS V32. + endchoice -# Etrax100 LX v1 has a MMU "feature" requiring a low mapping -config CRIS_LOW_MAP - bool - depends on ETRAX100LX - default y +config ETRAX_ARCH_V10 + bool + default y if ETRAX100LX || ETRAX100LX_V2 + default n if !(ETRAX100LX || ETRAX100LX_V2) -config ETRAX_DRAM_VIRTUAL_BASE - hex - default "c0000000" if !ETRAX100LX - default "60000000" if ETRAX100LX +config ETRAX_ARCH_V32 + bool + default y if ETRAX200LX + default n if !(ETRAX200LX) config ETRAX_DRAM_SIZE int "DRAM size (dec, in MB)" @@ -114,404 +138,17 @@ config ETRAX_ROOT_DEVICE should normally be the mtdblock device for the partition after the last partition in the partition table. -choice - prompt "Product LED port" - default ETRAX_PA_LEDS - -config ETRAX_PA_LEDS - bool "Port-PA-LEDs" - help - The Etrax network driver is responsible for flashing LED's when - packets arrive and are sent. It uses macros defined in - <file:include/asm-cris/io.h>, and those macros are defined after what - YOU choose in this option. The actual bits used are configured - separately. Select this if the LEDs are on port PA. Some products - put the leds on PB or a memory-mapped latch (CSP0) instead. - -config ETRAX_PB_LEDS - bool "Port-PB-LEDs" - help - The Etrax network driver is responsible for flashing LED's when - packets arrive and are sent. It uses macros defined in - <file:include/asm-cris/io.h>, and those macros are defined after what - YOU choose in this option. The actual bits used are configured - separately. Select this if the LEDs are on port PB. Some products - put the leds on PA or a memory-mapped latch (CSP0) instead. - -config ETRAX_CSP0_LEDS - bool "Port-CSP0-LEDs" - help - The Etrax network driver is responsible for flashing LED's when - packets arrive and are sent. It uses macros defined in - <file:include/asm-cris/io.h>, and those macros are defined after what - YOU choose in this option. The actual bits used are configured - separately. Select this if the LEDs are on a memory-mapped latch - using chip select CSP0, this is mapped at 0x90000000. - Some products put the leds on PA or PB instead. - -config ETRAX_NO_LEDS - bool "None" - help - Select this option if you don't have any LED at all. - -endchoice - -config ETRAX_LED1G - int "First green LED bit" - depends on !ETRAX_NO_LEDS - default "2" - help - Bit to use for the first green LED. - Most Axis products use bit 2 here. - -config ETRAX_LED1R - int "First red LED bit" - depends on !ETRAX_NO_LEDS - default "3" - help - Bit to use for the first red LED. - Most Axis products use bit 3 here. - For products with only one controllable LED, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED2G - int "Second green LED bit" - depends on !ETRAX_NO_LEDS - default "4" - help - Bit to use for the second green LED. The "Active" LED. - Most Axis products use bit 4 here. - For products with only one controllable LED, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED2R - int "Second red LED bit" - depends on !ETRAX_NO_LEDS - default "5" - help - Bit to use for the second red LED. - Most Axis products use bit 5 here. - For products with only one controllable LED, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED3G - int "Third green LED bit" - depends on !ETRAX_NO_LEDS - default "2" - help - Bit to use for the third green LED. The "Drive" LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED3R - int "Third red LED bit" - depends on !ETRAX_NO_LEDS - default "2" - help - Bit to use for the third red LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED4R - int "Fourth red LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the fourth red LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED4G - int "Fourth green LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the fourth green LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). +# duplicate choice configs are not yet supported, so the followinguse +# doesn't work: -config ETRAX_LED5R - int "Fifth red LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the fifth red LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). +source arch/cris/arch-v10/Kconfig -config ETRAX_LED5G - int "Fifth green LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the fifth green LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED6R - int "Sixth red LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the sixth red LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED6G - int "Sixth green LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the sixth green LED. The "Drive" LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED7R - int "Seventh red LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the seventh red LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED7G - int "Seventh green LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the seventh green LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED8Y - int "Eigth yellow LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the eighth yellow LED. The "Drive" LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED9Y - int "Ninth yellow LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the ninth yellow LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED10Y - int "Tenth yellow LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the tenth yellow LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED11Y - int "Eleventh yellow LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the eleventh yellow LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -config ETRAX_LED12R - int "Twelfth red LED bit" - depends on ETRAX_CSP0_LEDS - default "2" - help - Bit to use for the twelfth red LED. - For products with only one or two controllable LEDs, - set this to same as CONFIG_ETRAX_LED1G (normally 2). - -choice - prompt "Product debug-port" - default ETRAX_DEBUG_PORT0 - -config ETRAX_DEBUG_PORT0 - bool "Serial-0" - help - Choose a serial port for the ETRAX debug console. Default to - port 0. - -config ETRAX_DEBUG_PORT1 - bool "Serial-1" - help - Use serial port 1 for the console. - -config ETRAX_DEBUG_PORT2 - bool "Serial-2" - help - Use serial port 2 for the console. - -config ETRAX_DEBUG_PORT3 - bool "Serial-3" - help - Use serial port 3 for the console. - -config ETRAX_DEBUG_PORT_NULL - bool "disabled" - help - Disable serial-port debugging. - -endchoice - -choice - prompt "Product rescue-port" - default ETRAX_RESCUE_SER0 - -config ETRAX_RESCUE_SER0 - bool "Serial-0" - help - Select one of the four serial ports as a rescue port. The default - is port 0. - -config ETRAX_RESCUE_SER1 - bool "Serial-1" - help - Use serial port 1 as the rescue port. - -config ETRAX_RESCUE_SER2 - bool "Serial-2" - help - Use serial port 2 as the rescue port. - -config ETRAX_RESCUE_SER3 - bool "Serial-3" - help - Use serial port 3 as the rescue port. - -endchoice - -config ETRAX_DEF_R_WAITSTATES - hex "R_WAITSTATES" - default "95a6" - help - Waitstates for SRAM, Flash and peripherials (not DRAM). 95f8 is a - good choice for most Axis products... - -config ETRAX_DEF_R_BUS_CONFIG - hex "R_BUS_CONFIG" - default "104" - help - Assorted bits controlling write mode, DMA burst length etc. 104 is - a good choice for most Axis products... - -config ETRAX_SDRAM - bool "SDRAM support" - help - Enable this if you use SDRAM chips and configure - R_SDRAM_CONFIG and R_SDRAM_TIMING as well. - -config ETRAX_DEF_R_DRAM_CONFIG - hex "R_DRAM_CONFIG" - depends on !ETRAX_SDRAM - default "1a200040" - help - The R_DRAM_CONFIG register specifies everything on how the DRAM - chips in the system are connected to the Etrax CPU. This is - different depending on the manufacturer, chip type and number of - chips. So this value often needs to be different for each Axis - product. - -config ETRAX_DEF_R_DRAM_TIMING - hex "R_DRAM_TIMING" - depends on !ETRAX_SDRAM - default "5611" - help - Different DRAM chips have different speeds. Current Axis products - use 50ns DRAM chips which can use the timing: 5611. - -config ETRAX_DEF_R_SDRAM_CONFIG - hex "R_SDRAM_CONFIG" - depends on ETRAX_SDRAM - default "d2fa7878" - help - The R_SDRAM_CONFIG register specifies everything on how the SDRAM - chips in the system are connected to the Etrax CPU. This is - different depending on the manufacturer, chip type and number of - chips. So this value often needs to be different for each Axis - product. - -config ETRAX_DEF_R_SDRAM_TIMING - hex "R_SDRAM_TIMING" - depends on ETRAX_SDRAM - default "80004801" - help - Different SDRAM chips have different timing. - -config ETRAX_DEF_R_PORT_PA_DIR - hex "R_PORT_PA_DIR" - default "1c" - help - Configures the direction of general port A bits. 1 is out, 0 is in. - This is often totally different depending on the product used. - There are some guidelines though - if you know that only LED's are - connected to port PA, then they are usually connected to bits 2-4 - and you can therefore use 1c. On other boards which don't have the - LED's at the general ports, these bits are used for all kinds of - stuff. If you don't know what to use, it is always safe to put all - as inputs, although floating inputs isn't good. - -config ETRAX_DEF_R_PORT_PA_DATA - hex "R_PORT_PA_DATA" - default "00" - help - Configures the initial data for the general port A bits. Most - products should use 00 here. - -config ETRAX_DEF_R_PORT_PB_CONFIG - hex "R_PORT_PB_CONFIG" - default "00" - help - Configures the type of the general port B bits. 1 is chip select, - 0 is port. Most products should use 00 here. +endmenu -config ETRAX_DEF_R_PORT_PB_DIR - hex "R_PORT_PB_DIR" - default "00" - help - Configures the direction of general port B bits. 1 is out, 0 is in. - This is often totally different depending on the product used. Bits - 0 and 1 on port PB are usually used for I2C communication, but the - kernel I2C driver sets the appropriate directions itself so you - don't need to take that into consideration when setting this option. - If you don't know what to use, it is always safe to put all as - inputs. - -config ETRAX_DEF_R_PORT_PB_DATA - hex "R_PORT_PB_DATA" - default "ff" - help - Configures the initial data for the general port A bits. Most - products should use FF here. +# bring in ETRAX built-in drivers +menu "Drivers for built-in interfaces" -config ETRAX_SOFT_SHUTDOWN - bool "Software Shutdown Support" - help - Enable this if Etrax is used with a power-supply that can be turned - off and on with PS_ON signal. Gives the possibility to detect - powerbutton and then do a power off after unmounting disks. - -config ETRAX_SHUTDOWN_BIT - int "Shutdown bit on port CSP0" - depends on ETRAX_SOFT_SHUTDOWN - default "12" - help - Configure what pin on CSPO-port that is used for controlling power - supply. - -config ETRAX_POWERBUTTON_BIT - int "Power button bit on port G" - depends on ETRAX_SOFT_SHUTDOWN - default "25" - help - Configure where power button is connected. +source arch/cris/arch-v10/drivers/Kconfig endmenu diff --git a/arch/cris/Makefile b/arch/cris/Makefile index 94c3671bdf5c..3a1a7d919725 100644 --- a/arch/cris/Makefile +++ b/arch/cris/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.3 2002/01/21 15:21:23 bjornw Exp $ +# $Id: Makefile,v 1.15 2003/07/04 12:47:53 tobiasa Exp $ # cris/Makefile # # This file is included by the global makefile so that you can add your own @@ -13,37 +13,40 @@ # A bug in ld prevents us from having a (constant-value) symbol in a # "ORIGIN =" or "LENGTH =" expression. -LD = $(CROSS_COMPILE)ld -mcrislinux +arch-y := v10 +arch-$(CONFIG_ETRAX_ARCH_V10) := v10 + +# No config avaiable for make clean etc +ifneq ($(arch-y),) +SARCH := arch-$(arch-y) +else +SARCH := +endif -# objcopy is used to make binary images from the resulting linked file +LD = $(CROSS_COMPILE)ld -mcrislinux +LDFLAGS_BLOB := --format binary --oformat elf32-cris \ + -T arch/cris/$(SARCH)/output_arch.ld OBJCOPYFLAGS := -O binary -R .note -R .comment -S -# -mlinux enables -march=v10, -fno-underscores, -D__linux__ among others +AFLAGS_vmlinux.lds.o = -DDRAM_VIRTUAL_BASE=0x$(CONFIG_ETRAX_DRAM_VIRTUAL_BASE) +AFLAGS += -mlinux -CFLAGS := $(CFLAGS) -mlinux -pipe +CFLAGS := $(CFLAGS) -mlinux -march=$(arch-y) -pipe ifdef CONFIG_ETRAX_KGDB CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS)) -g CFLAGS += -fno-omit-frame-pointer endif -AFLAGS += -mlinux +HEAD := arch/$(ARCH)/$(SARCH)/kernel/head.o -HEAD := arch/cris/kernel/head.o - -ifdef CONFIG_ETRAX_AXISFLASHMAP -# only build this if axis flash map is used, because they depend on -# each others config options -SUBDIRS += arch/cris/boot/rescue -endif LIBGCC = $(shell $(CC) $(CFLAGS) -print-file-name=libgcc.a) -core-y += arch/cris/kernel/ arch/cris/mm/ -drivers-y += arch/cris/drivers/ -libs-y += arch/cris/lib/lib.a $(LIBGCC) - -MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot +core-y += arch/$(ARCH)/kernel/ arch/$(ARCH)/mm/ +core-y += arch/$(ARCH)/$(SARCH)/kernel/ arch/$(ARCH)/$(SARCH)/mm/ +drivers-y += arch/$(ARCH)/$(SARCH)/drivers/ +libs-y += arch/$(ARCH)/$(SARCH)/lib/ $(LIBGCC) vmlinux.bin: vmlinux $(OBJCOPY) $(OBJCOPYFLAGS) vmlinux vmlinux.bin @@ -65,20 +68,43 @@ cramfs: clinux: vmlinux.bin decompress.bin rescue.bin decompress.bin: FORCE - @make -C arch/cris/boot/compressed decompress.bin + @make -C arch/$(ARCH)/boot/compressed decompress.bin rescue.bin: FORCE - @make -C arch/cris/boot/rescue rescue.bin + @make -C arch/$(ARCH)/boot/rescue rescue.bin -zImage: vmlinux.bin +zImage: vmlinux.bin rescue.bin ## zImage - Compressed kernel (gzip) - @$(MAKEBOOT) zImage + @make -C arch/$(ARCH)/boot/ zImage compressed: zImage +archmrproper: archclean: - @$(MAKEBOOT) clean + $(Q)$(MAKE) -f scripts/Makefile.clean obj=arch/$(ARCH)/boot rm -f timage vmlinux.bin cramfs.img rm -rf $(LD_SCRIPT).tmp -archmrproper: +prepare: arch/$(ARCH)/.links include/asm-$(ARCH)/.arch \ + include/asm-$(ARCH)/$(SARCH)/offset.h + +# Create some links to make all tools happy +arch/$(ARCH)/.links: + @ln -sfn $(SARCH)/drivers arch/$(ARCH)/drivers + @ln -sfn $(SARCH)/boot arch/$(ARCH)/boot + @ln -sfn $(SARCH)/lib arch/$(ARCH)/lib + @ln -sfn $(SARCH)/vmlinux.lds.S arch/$(ARCH)/vmlinux.lds.S + @touch $@ + +# Create link to sub arch includes +include/asm-$(ARCH)/.arch: $(wildcard include/config/arch/*.h) + @echo ' Making asm-$(ARCH)/arch -> asm-$(ARCH)/$(SARCH) symlink' + @rm -f include/asm-$(ARCH)/arch + @ln -sf $(SARCH) include/asm-$(ARCH)/arch + @touch $@ + +arch/$(ARCH)/$(SARCH)/kernel/asm-offsets.s: include/asm include/linux/version.h \ + include/config/MARKER + +include/asm-$(ARCH)/$(SARCH)/offset.h: arch/$(ARCH)/$(SARCH)/kernel/asm-offsets.s + $(call filechk,gen-asm-offsets) diff --git a/arch/cris/arch-v10/Kconfig b/arch/cris/arch-v10/Kconfig new file mode 100644 index 000000000000..2ca64cc40c63 --- /dev/null +++ b/arch/cris/arch-v10/Kconfig @@ -0,0 +1,422 @@ +# ETRAX 100LX v1 has a MMU "feature" requiring a low mapping +config CRIS_LOW_MAP + bool + depends on ETRAX_ARCH_V10 && ETRAX100LX + default y + +config ETRAX_DRAM_VIRTUAL_BASE + hex + depends on ETRAX_ARCH_V10 + default "c0000000" if !ETRAX100LX + default "60000000" if ETRAX100LX + +choice + prompt "Product LED port" + depends on ETRAX_ARCH_V10 + default ETRAX_PA_LEDS + +config ETRAX_PA_LEDS + bool "Port-PA-LEDs" + help + The ETRAX network driver is responsible for flashing LED's when + packets arrive and are sent. It uses macros defined in + <file:include/asm-cris/io.h>, and those macros are defined after what + YOU choose in this option. The actual bits used are configured + separately. Select this if the LEDs are on port PA. Some products + put the leds on PB or a memory-mapped latch (CSP0) instead. + +config ETRAX_PB_LEDS + bool "Port-PB-LEDs" + help + The ETRAX network driver is responsible for flashing LED's when + packets arrive and are sent. It uses macros defined in + <file:include/asm-cris/io.h>, and those macros are defined after what + YOU choose in this option. The actual bits used are configured + separately. Select this if the LEDs are on port PB. Some products + put the leds on PA or a memory-mapped latch (CSP0) instead. + +config ETRAX_CSP0_LEDS + bool "Port-CSP0-LEDs" + help + The ETRAX network driver is responsible for flashing LED's when + packets arrive and are sent. It uses macros defined in + <file:include/asm-cris/io.h>, and those macros are defined after what + YOU choose in this option. The actual bits used are configured + separately. Select this if the LEDs are on a memory-mapped latch + using chip select CSP0, this is mapped at 0x90000000. + Some products put the leds on PA or PB instead. + +config ETRAX_NO_LEDS + bool "None" + help + Select this option if you don't have any LED at all. + +endchoice + +config ETRAX_LED1G + int "First green LED bit" + depends on ETRAX_ARCH_V10 && !ETRAX_NO_LEDS + default "2" + help + Bit to use for the first green LED. + Most Axis products use bit 2 here. + +config ETRAX_LED1R + int "First red LED bit" + depends on ETRAX_ARCH_V10 && !ETRAX_NO_LEDS + default "3" + help + Bit to use for the first red LED. + Most Axis products use bit 3 here. + For products with only one controllable LED, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED2G + int "Second green LED bit" + depends on ETRAX_ARCH_V10 && !ETRAX_NO_LEDS + default "4" + help + Bit to use for the second green LED. The "Active" LED. + Most Axis products use bit 4 here. + For products with only one controllable LED, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED2R + int "Second red LED bit" + depends on ETRAX_ARCH_V10 && !ETRAX_NO_LEDS + default "5" + help + Bit to use for the second red LED. + Most Axis products use bit 5 here. + For products with only one controllable LED, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED3G + int "Third green LED bit" + depends on ETRAX_ARCH_V10 && !ETRAX_NO_LEDS + default "2" + help + Bit to use for the third green LED. The "Drive" LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED3R + int "Third red LED bit" + depends on ETRAX_ARCH_V10 && !ETRAX_NO_LEDS + default "2" + help + Bit to use for the third red LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED4R + int "Fourth red LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the fourth red LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED4G + int "Fourth green LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the fourth green LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED5R + int "Fifth red LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the fifth red LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED5G + int "Fifth green LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the fifth green LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED6R + int "Sixth red LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the sixth red LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED6G + int "Sixth green LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the sixth green LED. The "Drive" LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED7R + int "Seventh red LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the seventh red LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED7G + int "Seventh green LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the seventh green LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED8Y + int "Eigth yellow LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the eighth yellow LED. The "Drive" LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED9Y + int "Ninth yellow LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the ninth yellow LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED10Y + int "Tenth yellow LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the tenth yellow LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED11Y + int "Eleventh yellow LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the eleventh yellow LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +config ETRAX_LED12R + int "Twelfth red LED bit" + depends on ETRAX_CSP0_LEDS + default "2" + help + Bit to use for the twelfth red LED. + For products with only one or two controllable LEDs, + set this to same as CONFIG_ETRAX_LED1G (normally 2). + +choice + prompt "Product debug-port" + depends on ETRAX_ARCH_V10 + default ETRAX_DEBUG_PORT0 + +config ETRAX_DEBUG_PORT0 + bool "Serial-0" + help + Choose a serial port for the ETRAX debug console. Default to + port 0. + +config ETRAX_DEBUG_PORT1 + bool "Serial-1" + help + Use serial port 1 for the console. + +config ETRAX_DEBUG_PORT2 + bool "Serial-2" + help + Use serial port 2 for the console. + +config ETRAX_DEBUG_PORT3 + bool "Serial-3" + help + Use serial port 3 for the console. + +config ETRAX_DEBUG_PORT_NULL + bool "disabled" + help + Disable serial-port debugging. + +endchoice + +choice + prompt "Product rescue-port" + depends on ETRAX_ARCH_V10 + default ETRAX_RESCUE_SER0 + +config ETRAX_RESCUE_SER0 + bool "Serial-0" + help + Select one of the four serial ports as a rescue port. The default + is port 0. + +config ETRAX_RESCUE_SER1 + bool "Serial-1" + help + Use serial port 1 as the rescue port. + +config ETRAX_RESCUE_SER2 + bool "Serial-2" + help + Use serial port 2 as the rescue port. + +config ETRAX_RESCUE_SER3 + bool "Serial-3" + help + Use serial port 3 as the rescue port. + +endchoice + +config ETRAX_DEF_R_WAITSTATES + hex "R_WAITSTATES" + depends on ETRAX_ARCH_V10 + default "95a6" + help + Waitstates for SRAM, Flash and peripherials (not DRAM). 95f8 is a + good choice for most Axis products... + +config ETRAX_DEF_R_BUS_CONFIG + hex "R_BUS_CONFIG" + depends on ETRAX_ARCH_V10 + default "104" + help + Assorted bits controlling write mode, DMA burst length etc. 104 is + a good choice for most Axis products... + +config ETRAX_SDRAM + bool "SDRAM support" + depends on ETRAX_ARCH_V10 + help + Enable this if you use SDRAM chips and configure + R_SDRAM_CONFIG and R_SDRAM_TIMING as well. + +config ETRAX_DEF_R_DRAM_CONFIG + hex "R_DRAM_CONFIG" + depends on ETRAX_ARCH_V10 && !ETRAX_SDRAM + default "1a200040" + help + The R_DRAM_CONFIG register specifies everything on how the DRAM + chips in the system are connected to the ETRAX CPU. This is + different depending on the manufacturer, chip type and number of + chips. So this value often needs to be different for each Axis + product. + +config ETRAX_DEF_R_DRAM_TIMING + hex "R_DRAM_TIMING" + depends on ETRAX_ARCH_V10 && !ETRAX_SDRAM + default "5611" + help + Different DRAM chips have different speeds. Current Axis products + use 50ns DRAM chips which can use the timing: 5611. + +config ETRAX_DEF_R_SDRAM_CONFIG + hex "R_SDRAM_CONFIG" + depends on ETRAX_ARCH_V10 && ETRAX_SDRAM + default "d2fa7878" + help + The R_SDRAM_CONFIG register specifies everything on how the SDRAM + chips in the system are connected to the ETRAX CPU. This is + different depending on the manufacturer, chip type and number of + chips. So this value often needs to be different for each Axis + product. + +config ETRAX_DEF_R_SDRAM_TIMING + hex "R_SDRAM_TIMING" + depends on ETRAX_ARCH_V10 && ETRAX_SDRAM + default "80004801" + help + Different SDRAM chips have different timing. + +config ETRAX_DEF_R_PORT_PA_DIR + hex "R_PORT_PA_DIR" + depends on ETRAX_ARCH_V10 + default "1c" + help + Configures the direction of general port A bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_R_PORT_PA_DATA + hex "R_PORT_PA_DATA" + depends on ETRAX_ARCH_V10 + default "00" + help + Configures the initial data for the general port A bits. Most + products should use 00 here. + +config ETRAX_DEF_R_PORT_PB_CONFIG + hex "R_PORT_PB_CONFIG" + depends on ETRAX_ARCH_V10 + default "00" + help + Configures the type of the general port B bits. 1 is chip select, + 0 is port. Most products should use 00 here. + +config ETRAX_DEF_R_PORT_PB_DIR + hex "R_PORT_PB_DIR" + depends on ETRAX_ARCH_V10 + default "00" + help + Configures the direction of general port B bits. 1 is out, 0 is in. + This is often totally different depending on the product used. Bits + 0 and 1 on port PB are usually used for I2C communication, but the + kernel I2C driver sets the appropriate directions itself so you + don't need to take that into consideration when setting this option. + If you don't know what to use, it is always safe to put all as + inputs. + +config ETRAX_DEF_R_PORT_PB_DATA + hex "R_PORT_PB_DATA" + depends on ETRAX_ARCH_V10 + default "ff" + help + Configures the initial data for the general port A bits. Most + products should use FF here. + +config ETRAX_SOFT_SHUTDOWN + bool "Software Shutdown Support" + depends on ETRAX_ARCH_V10 + help + Enable this if ETRAX is used with a power-supply that can be turned + off and on with PS_ON signal. Gives the possibility to detect + powerbutton and then do a power off after unmounting disks. + +config ETRAX_SHUTDOWN_BIT + int "Shutdown bit on port CSP0" + depends on ETRAX_SOFT_SHUTDOWN + default "12" + help + Configure what pin on CSPO-port that is used for controlling power + supply. + +config ETRAX_POWERBUTTON_BIT + int "Power button bit on port G" + depends on ETRAX_SOFT_SHUTDOWN + default "25" + help + Configure where power button is connected. diff --git a/arch/cris/README.mm b/arch/cris/arch-v10/README.mm index 195ca898b55b..6f08903f3139 100644 --- a/arch/cris/README.mm +++ b/arch/cris/arch-v10/README.mm @@ -3,8 +3,8 @@ Memory management for CRIS/MMU HISTORY: $Log: README.mm,v $ -Revision 1.1.1.1 2001/12/17 13:59:27 bjornw -Import of Linux 2.5.1 +Revision 1.1 2001/12/17 13:59:27 bjornw +Initial revision Revision 1.1 2000/07/10 16:25:21 bjornw Initial revision diff --git a/arch/cris/boot/Makefile b/arch/cris/arch-v10/boot/Makefile index fe6650368e6a..fe6650368e6a 100644 --- a/arch/cris/boot/Makefile +++ b/arch/cris/arch-v10/boot/Makefile diff --git a/arch/cris/boot/compressed/Makefile b/arch/cris/arch-v10/boot/compressed/Makefile index 5f71c2c819e6..5f71c2c819e6 100644 --- a/arch/cris/boot/compressed/Makefile +++ b/arch/cris/arch-v10/boot/compressed/Makefile diff --git a/arch/cris/boot/compressed/README b/arch/cris/arch-v10/boot/compressed/README index 349eb2ab4663..48b3db9924b9 100644 --- a/arch/cris/boot/compressed/README +++ b/arch/cris/arch-v10/boot/compressed/README @@ -1,6 +1,6 @@ Creation of the self-extracting compressed kernel image (vmlinuz) ----------------------------------------------------------------- -$Id: README,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +$Id: README,v 1.1 2001/12/17 13:59:27 bjornw Exp $ This can be slightly confusing because it's a process with many steps. diff --git a/arch/cris/boot/compressed/decompress.ld b/arch/cris/arch-v10/boot/compressed/decompress.ld index 24be54924970..0b0a14fe6177 100644 --- a/arch/cris/boot/compressed/decompress.ld +++ b/arch/cris/arch-v10/boot/compressed/decompress.ld @@ -13,6 +13,7 @@ SECTIONS _stext = . ; *(.text) *(.rodata) + *(.rodata.*) _etext = . ; } > dram .data : diff --git a/arch/cris/boot/compressed/head.S b/arch/cris/arch-v10/boot/compressed/head.S index 84719ff14401..4cbdd4b1d9d6 100644 --- a/arch/cris/boot/compressed/head.S +++ b/arch/cris/arch-v10/boot/compressed/head.S @@ -10,7 +10,7 @@ #include <linux/config.h> #define ASSEMBLER_MACROS_ONLY -#include <asm/sv_addr_ag.h> +#include <asm/arch/sv_addr_ag.h> #define RAM_INIT_MAGIC 0x56902387 diff --git a/arch/cris/boot/compressed/misc.c b/arch/cris/arch-v10/boot/compressed/misc.c index 906610b45625..9969c6242f75 100644 --- a/arch/cris/boot/compressed/misc.c +++ b/arch/cris/arch-v10/boot/compressed/misc.c @@ -1,7 +1,7 @@ /* * misc.c * - * $Id: misc.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ + * $Id: misc.c,v 1.4 2003/04/09 05:20:45 starvik Exp $ * * This is a collection of several routines from gzip-1.0.3 * adapted for Linux. @@ -15,7 +15,7 @@ /* where the piggybacked kernel image expects itself to live. * it is the same address we use when we network load an uncompressed * image into DRAM, and it is the address the kernel is linked to live - * at by etrax100.ld. + * at by vmlinux.lds.S */ #define KERNEL_LOAD_ADR 0x40004000 @@ -23,7 +23,7 @@ #include <linux/config.h> #include <linux/types.h> -#include <asm/svinto.h> +#include <asm/arch/svinto.h> /* * gzip declarations @@ -109,7 +109,7 @@ static void puts(const char *); extern int end; static long free_mem_ptr = (long)&end; -#include "../../../../lib/inflate.c" +#include "../../../../../lib/inflate.c" static void *malloc(int size) { diff --git a/arch/cris/boot/rescue/Makefile b/arch/cris/arch-v10/boot/rescue/Makefile index e9f2ba2ad02c..e9f2ba2ad02c 100644 --- a/arch/cris/boot/rescue/Makefile +++ b/arch/cris/arch-v10/boot/rescue/Makefile diff --git a/arch/cris/boot/rescue/head.S b/arch/cris/arch-v10/boot/rescue/head.S index 52881742da43..8689ea972c46 100644 --- a/arch/cris/boot/rescue/head.S +++ b/arch/cris/arch-v10/boot/rescue/head.S @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.2 2001/12/18 13:35:12 bjornw Exp $ +/* $Id: head.S,v 1.6 2003/04/09 08:12:43 pkj Exp $ * * Rescue code, made to reside at the beginning of the * flash-memory. when it starts, it checks a partition @@ -52,7 +52,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,12 +60,12 @@ * 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 <linux/config.h> #define ASSEMBLER_MACROS_ONLY -#include <asm/sv_addr_ag.h> +#include <asm/arch/sv_addr_ag.h> ;; The partitiontable is looked for at the first sector after the boot ;; sector. Sector size is 65536 bytes in all flashes we use. @@ -192,7 +192,7 @@ no_newjump: 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 diff --git a/arch/cris/boot/rescue/kimagerescue.S b/arch/cris/arch-v10/boot/rescue/kimagerescue.S index 270774108b7e..264bf7afc9ad 100644 --- a/arch/cris/boot/rescue/kimagerescue.S +++ b/arch/cris/arch-v10/boot/rescue/kimagerescue.S @@ -1,4 +1,4 @@ -/* $Id: kimagerescue.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: kimagerescue.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ * * Rescue code to be prepended on a kimage and copied to the * rescue serial port. diff --git a/arch/cris/boot/rescue/rescue.ld b/arch/cris/arch-v10/boot/rescue/rescue.ld index 0b52a9490db6..0b52a9490db6 100644 --- a/arch/cris/boot/rescue/rescue.ld +++ b/arch/cris/arch-v10/boot/rescue/rescue.ld diff --git a/arch/cris/boot/rescue/testrescue.S b/arch/cris/arch-v10/boot/rescue/testrescue.S index f39c69e26483..566a9f341254 100644 --- a/arch/cris/boot/rescue/testrescue.S +++ b/arch/cris/arch-v10/boot/rescue/testrescue.S @@ -1,4 +1,4 @@ -/* $Id: testrescue.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: testrescue.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ * * Simple testcode to download by the rescue block. * Just lits some LEDs to show it was downloaded correctly. diff --git a/arch/cris/boot/tools/build.c b/arch/cris/arch-v10/boot/tools/build.c index 2f9bbb26d603..2f9bbb26d603 100644 --- a/arch/cris/boot/tools/build.c +++ b/arch/cris/arch-v10/boot/tools/build.c diff --git a/arch/cris/arch-v10/defconfig b/arch/cris/arch-v10/defconfig new file mode 100644 index 000000000000..9d40dd316576 --- /dev/null +++ b/arch/cris/arch-v10/defconfig @@ -0,0 +1,506 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# General setup +# +CONFIG_NET=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_SYSCTL is not set +CONFIG_BINFMT_ELF=y +# CONFIG_ETRAX_KGDB is not set +# CONFIG_ETRAX_WATCHDOG is not set + +# +# Hardware setup +# +CONFIG_ETRAX100LX=y +# CONFIG_ETRAX100LX_V2 is not set +# CONFIG_SVINTO_SIM is not set +CONFIG_CRIS_LOW_MAP=y +CONFIG_ETRAX_DRAM_VIRTUAL_BASE=60000000 +CONFIG_ETRAX_DRAM_SIZE=8 +CONFIG_ETRAX_FLASH_BUSWIDTH=2 +CONFIG_ETRAX_ROOT_DEVICE="/dev/mtdblock3" +CONFIG_ETRAX_PA_LEDS=y +# CONFIG_ETRAX_PB_LEDS is not set +# CONFIG_ETRAX_CSP0_LEDS is not set +# CONFIG_ETRAX_NO_LEDS is not set +CONFIG_ETRAX_LED1G=2 +CONFIG_ETRAX_LED1R=2 +CONFIG_ETRAX_LED2G=2 +CONFIG_ETRAX_LED2R=2 +CONFIG_ETRAX_LED3R=2 +CONFIG_ETRAX_LED3G=2 +CONFIG_ETRAX_LED4R=2 +CONFIG_ETRAX_LED4G=2 +CONFIG_ETRAX_LED5R=2 +CONFIG_ETRAX_LED5G=2 +CONFIG_ETRAX_LED6R=2 +CONFIG_ETRAX_LED6G=2 +CONFIG_ETRAX_LED7R=2 +CONFIG_ETRAX_LED7G=2 +CONFIG_ETRAX_LED8Y=2 +CONFIG_ETRAX_LED9Y=2 +CONFIG_ETRAX_LED10Y=2 +CONFIG_ETRAX_LED11Y=2 +CONFIG_ETRAX_LED12R=2 +CONFIG_ETRAX_DEBUG_PORT0=y +# CONFIG_ETRAX_DEBUG_PORT1 is not set +# CONFIG_ETRAX_DEBUG_PORT2 is not set +# CONFIG_ETRAX_DEBUG_PORT3 is not set +CONFIG_ETRAX_RESCUE_SER0=y +# CONFIG_ETRAX_RESCUE_SER1 is not set +# CONFIG_ETRAX_RESCUE_SER2 is not set +# CONFIG_ETRAX_RESCUE_SER3 is not set +CONFIG_ETRAX_DEF_R_WAITSTATES=95a6 +CONFIG_ETRAX_DEF_R_BUS_CONFIG=104 +# CONFIG_ETRAX_SDRAM is not set +CONFIG_ETRAX_DEF_R_DRAM_CONFIG=1a200040 +CONFIG_ETRAX_DEF_R_DRAM_TIMING=5611 +CONFIG_ETRAX_DEF_R_PORT_PA_DIR=1d +CONFIG_ETRAX_DEF_R_PORT_PA_DATA=f0 +CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG=00 +CONFIG_ETRAX_DEF_R_PORT_PB_DIR=1e +CONFIG_ETRAX_DEF_R_PORT_PB_DATA=f3 +# CONFIG_ETRAX_SOFT_SHUTDOWN is not set + +# +# Drivers for ETRAX 100LX built-in interfaces +# +CONFIG_ETRAX_ETHERNET=y +CONFIG_NET_ETHERNET=y +# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set +CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y +# CONFIG_ETRAX_ETHERNET_LPSLAVE is not set +CONFIG_ETRAX_SERIAL=y +CONFIG_ETRAX_SERIAL_PORT0=y +# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB is not set +CONFIG_ETRAX_SERIAL_PORT1=y +# CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB is not set +# CONFIG_ETRAX_SERIAL_PORT2 is not set +# CONFIG_ETRAX_SERIAL_PORT3 is not set +# CONFIG_ETRAX_RS485 is not set +# CONFIG_ETRAX_SYNCHRONOUS_SERIAL is not set +# CONFIG_ETRAX_IDE is not set +CONFIG_ETRAX_AXISFLASHMAP=y +CONFIG_ETRAX_PTABLE_SECTOR=65536 +CONFIG_MTD=y +CONFIG_MTD_CFI=y +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_AMDSTD=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_ETRAX_I2C=y +CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C=y +# CONFIG_ETRAX_I2C_EEPROM is not set +CONFIG_ETRAX_GPIO=y +CONFIG_ETRAX_PA_BUTTON_BITMASK=02 +CONFIG_ETRAX_PA_CHANGEABLE_DIR=00 +CONFIG_ETRAX_PA_CHANGEABLE_BITS=FF +CONFIG_ETRAX_PB_CHANGEABLE_DIR=00 +CONFIG_ETRAX_PB_CHANGEABLE_BITS=FF +# CONFIG_ETRAX_USB_HOST is not set +# CONFIG_USB is not set +# CONFIG_ETRAX_DS1302 is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC1000 is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOCPROBE is not set + +# +# RAM/ROM Device Drivers +# +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_MTDRAM is not set + +# +# Linearly Mapped Flash Device Drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_CFI_GEOMETRY is not set +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_AMDSTD=y +# CONFIG_MTD_SHARP is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_NORA is not set +# CONFIG_MTD_PNC2000 is not set +# CONFIG_MTD_RPXLITE is not set +# CONFIG_MTD_SC520CDP is not set +# CONFIG_MTD_SBC_MEDIAGX is not set +# CONFIG_MTD_ELAN_104NC is not set +# CONFIG_MTD_SA1100 is not set +# CONFIG_MTD_DC21285 is not set +# CONFIG_MTD_CSTM_CFI_JEDEC is not set +# CONFIG_MTD_JEDEC is not set +# CONFIG_MTD_MIXMEM is not set +# CONFIG_MTD_OCTAGON is not set +# CONFIG_MTD_VMAX is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_NAND_SPIA is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set +# CONFIG_PHONE_IXJ is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# IDE, ATA and ATAPI Block devices +# +# CONFIG_BLK_DEV_IDE is not set +# CONFIG_BLK_DEV_HD_IDE is not set +# CONFIG_BLK_DEV_HD is not set +# CONFIG_BLK_DEV_IDEDISK is not set +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_BLK_DEV_IDECS is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_CMD640_ENHANCED is not set +# CONFIG_BLK_DEV_ISAPNP is not set +# CONFIG_IDE_CHIPSETS is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_IDE_MODES is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_BLOCK is not set +# CONFIG_I2O_LAN is not set +# CONFIG_I2O_SCSI is not set +# CONFIG_I2O_PROC is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Input core support +# +# CONFIG_INPUT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_UNIX98_PTYS is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_CRAMFS=y +CONFIG_RAMFS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_DEBUG is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_EXT2_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_V3 is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_MOUNT_SUBDIR is not set +# CONFIG_NCPFS_NDS_DOMAINS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Kernel hacking +# +# CONFIG_PROFILE is not set diff --git a/arch/cris/drivers/Kconfig b/arch/cris/arch-v10/drivers/Kconfig index fef010d37227..7b0684d07fcf 100644 --- a/arch/cris/drivers/Kconfig +++ b/arch/cris/arch-v10/drivers/Kconfig @@ -1,8 +1,6 @@ - -menu "Drivers for ETRAX 100LX built-in interfaces" - config ETRAX_ETHERNET bool "Ethernet support" + depends on ETRAX_ARCH_V10 help This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet controller. @@ -45,51 +43,39 @@ choice config ETRAX_NETWORK_LED_ON_WHEN_LINK bool "LED_on_when_link" help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. + Selecting LED_on_when_link will light the LED when there is a + connection and will flash off when there is activity. - Selecting LED_on_when_activity will light the LED only when + Selecting LED_on_when_activity will light the LED only when there is activity. - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. + This setting will also affect the behaviour of other activity LEDs + e.g. Bluetooth. config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY bool "LED_on_when_activity" help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. + Selecting LED_on_when_link will light the LED when there is a + connection and will flash off when there is activity. - Selecting LED_on_when_activity will light the LED only when + Selecting LED_on_when_activity will light the LED only when there is activity. - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. + This setting will also affect the behaviour of other activity LEDs + e.g. Bluetooth. endchoice -config ETRAX_ETHERNET_LPSLAVE - bool "Etrax Ethernet slave support (over lp0/1)" - help - This option enables a slave ETRAX 100 or ETRAX 100LX, connected to a - master ETRAX 100 or ETRAX 100LX through par0 and par1, to act as an - Ethernet controller. - -config ETRAX_ETHERNET_LPSLAVE_HAS_LEDS - bool "Slave has its own LEDs" - depends on ETRAX_ETHERNET_LPSLAVE - help - Enable if the slave has it's own LEDs. - config ETRAX_SERIAL bool "Serial-port support" + depends on ETRAX_ARCH_V10 help Enables the ETRAX 100 serial driver for ser0 (ttyS0) You probably want this enabled. -# bool ' Use fast timers for DMA flush and RS-485 timing' CONFIG_ETRAX_SERIAL_FAST_TIMER n config ETRAX_SERIAL_FAST_TIMER - bool + bool "Use fast timers for serial DMA flush (experimental)" + depends on ETRAX_SERIAL help Select this to have the serial DMAs flushed at a higher rate than normally, possible by using the fast timer API, the timeout is @@ -107,7 +93,7 @@ config ETRAX_SERIAL_FLUSH_DMA_FAST config ETRAX_SERIAL_RX_TIMEOUT_TICKS int "Receive flush timeout (ticks) " - depends on ETRAX_SERIAL && !ETRAX_SERIAL_FAST_TIMER && !ETRAX100_SERIAL_FLUSH_DMA_FAST + depends on ETRAX_SERIAL && !ETRAX_SERIAL_FAST_TIMER && !ETRAX_SERIAL_FLUSH_DMA_FAST default "5" help Number of timer ticks between flush of receive fifo (1 tick = 10ms). @@ -123,41 +109,84 @@ config ETRAX_SERIAL_PORT0 Normally you want this on, unless you use external DMA 1 that uses the same DMA channels. -config ETRAX_SER0_DTR_RI_DSR_CD_ON_PB - bool "Ser0 DTR, RI, DSR, CD on PB" +choice + prompt "Ser0 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT0 + default ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE + +config ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE + bool "No_DTR_RI_DSR_CD" + +config ETRAX_SER0_DTR_RI_DSR_CD_ON_PA + bool "DTR_RI_DSR_CD_on_PA" + +config ETRAX_SER0_DTR_RI_DSR_CD_ON_PB + bool "DTR_RI_DSR_CD_on_PB" help Enables the status and control signals DTR, RI, DSR and CD on PB for ser0. +config ETRAX_SER0_DTR_RI_DSR_CD_MIXED + bool "DTR_RI_DSR_CD_mixed_on_PA_and_PB" + +endchoice + +config ETRAX_SER0_DTR_ON_PA_BIT + int "Ser0 DTR on PA bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT0 + default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED + default "4" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED + +config ETRAX_SER0_RI_ON_PA_BIT + int "Ser0 RI on PA bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT0 + default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED + default "5" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED + +config ETRAX_SER0_DSR_ON_PA_BIT + int "Ser0 DSR on PA bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT0 + default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED + default "6" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED + +config ETRAX_SER0_CD_ON_PA_BIT + int "Ser0 CD on PA bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT0 + default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED + default "7" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PA || ETRAX_SER0_DTR_RI_DSR_CD_MIXED + config ETRAX_SER0_DTR_ON_PB_BIT - int "Ser0 DTR on PB bit" - depends on ETRAX_SER0_DTR_RI_DSR_CD_ON_PB - default "4" + int "Ser0 DTR on PB bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT0 + default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED + default "4" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED help Specify the pin of the PB port to carry the DTR signal for serial port 0. config ETRAX_SER0_RI_ON_PB_BIT - int "Ser0 RI on PB bit" - depends on ETRAX_SER0_DTR_RI_DSR_CD_ON_PB - default "5" + int "Ser0 RI on PB bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT0 + default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED + default "5" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED help Specify the pin of the PB port to carry the RI signal for serial port 0. config ETRAX_SER0_DSR_ON_PB_BIT - int "Ser0 DSR on PB bit" - depends on ETRAX_SER0_DTR_RI_DSR_CD_ON_PB - default "6" + int "Ser0 DSR on PB bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT0 + default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED + default "6" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED help Specify the pin of the PB port to carry the DSR signal for serial port 0. config ETRAX_SER0_CD_ON_PB_BIT - int "Ser0 CD on PB bit" - depends on ETRAX_SER0_DTR_RI_DSR_CD_ON_PB - default "7" + int "Ser0 CD on PB bit (-1 = not used)" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT0 + default "-1" if !ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER0_DTR_RI_DSR_CD_MIXED + default "7" if ETRAX_SER0_DTR_RI_DSR_CD_ON_PB || ETRAX_SER0_DTR_RI_DSR_CD_MIXED help Specify the pin of the PB port to carry the CD signal for serial port 0. @@ -168,41 +197,84 @@ config ETRAX_SERIAL_PORT1 help Enables the ETRAX 100 serial driver for ser1 (ttyS1). -config ETRAX_SER1_DTR_RI_DSR_CD_ON_PB - bool "Ser1 DTR, RI, DSR, CD on PB" +choice + prompt "Ser1 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT1 + default ETRAX_SER1_DTR_RI_DSR_CD_ON_NONE + +config ETRAX_SER1_DTR_RI_DSR_CD_ON_NONE + bool "No_DTR_RI_DSR_CD" + +config ETRAX_SER1_DTR_RI_DSR_CD_ON_PA + bool "DTR_RI_DSR_CD_on_PA" + +config ETRAX_SER1_DTR_RI_DSR_CD_ON_PB + bool "DTR_RI_DSR_CD_on_PB" help Enables the status and control signals DTR, RI, DSR and CD on PB for ser1. +config ETRAX_SER1_DTR_RI_DSR_CD_MIXED + bool "DTR_RI_DSR_CD_mixed_on_PA_and_PB" + +endchoice + +config ETRAX_SER1_DTR_ON_PA_BIT + int "Ser1 DTR on PA bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT1 + default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED + default "4" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED + +config ETRAX_SER1_RI_ON_PA_BIT + int "Ser1 RI on PA bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT1 + default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED + default "5" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED + +config ETRAX_SER1_DSR_ON_PA_BIT + int "Ser1 DSR on PA bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT1 + default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED + default "6" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED + +config ETRAX_SER1_CD_ON_PA_BIT + int "Ser1 CD on PA bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT1 + default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED + default "7" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PA || ETRAX_SER1_DTR_RI_DSR_CD_MIXED + config ETRAX_SER1_DTR_ON_PB_BIT - int "Ser1 DTR on PB bit" - depends on ETRAX_SER1_DTR_RI_DSR_CD_ON_PB - default "4" + int "Ser1 DTR on PB bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT1 + default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED + default "4" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED help Specify the pin of the PB port to carry the DTR signal for serial port 1. config ETRAX_SER1_RI_ON_PB_BIT - int "Ser1 RI on PB bit" - depends on ETRAX_SER1_DTR_RI_DSR_CD_ON_PB - default "5" + int "Ser1 RI on PB bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT1 + default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED + default "5" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED help Specify the pin of the PB port to carry the RI signal for serial port 1. config ETRAX_SER1_DSR_ON_PB_BIT - int "Ser1 DSR on PB bit" - depends on ETRAX_SER1_DTR_RI_DSR_CD_ON_PB - default "6" + int "Ser1 DSR on PB bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT1 + default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED + default "6" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED help Specify the pin of the PB port to carry the DSR signal for serial port 1. config ETRAX_SER1_CD_ON_PB_BIT - int "Ser1 CD on PB bit" - depends on ETRAX_SER1_DTR_RI_DSR_CD_ON_PB - default "7" + int "Ser1 CD on PB bit (-1 = not used)" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT1 + default "-1" if !ETRAX_SER1_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER1_DTR_RI_DSR_CD_MIXED + default "7" if ETRAX_SER1_DTR_RI_DSR_CD_ON_PB || ETRAX_SER1_DTR_RI_DSR_CD_MIXED help Specify the pin of the PB port to carry the CD signal for serial port 1. @@ -216,51 +288,153 @@ config ETRAX_SERIAL_PORT2 help Enables the ETRAX 100 serial driver for ser2 (ttyS2). -config ETRAX_SER2_DTR_RI_DSR_CD_ON_PA - bool "Ser2 DTR, RI, DSR, CD on PA" +choice + prompt "Ser2 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT2 + default ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE + +config ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE + bool "No_DTR_RI_DSR_CD" + +config ETRAX_SER2_DTR_RI_DSR_CD_ON_PA + bool "DTR_RI_DSR_CD_on_PA" help Enables the status and control signals DTR, RI, DSR and CD on PA for ser2. +config ETRAX_SER2_DTR_RI_DSR_CD_ON_PB + bool "DTR_RI_DSR_CD_on_PB" + +config ETRAX_SER2_DTR_RI_DSR_CD_MIXED + bool "DTR_RI_DSR_CD_mixed_on_PA_and_PB" + +endchoice + config ETRAX_SER2_DTR_ON_PA_BIT - int "Ser2 DTR on PA bit" - depends on ETRAX_SER2_DTR_RI_DSR_CD_ON_PA - default "4" + int "Ser2 DTR on PA bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT2 + default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED + default "4" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED help Specify the pin of the PA port to carry the DTR signal for serial port 2. config ETRAX_SER2_RI_ON_PA_BIT - int "Ser2 RI on PA bit" - depends on ETRAX_SER2_DTR_RI_DSR_CD_ON_PA - default "5" + int "Ser2 RI on PA bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT2 + default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED + default "5" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED help Specify the pin of the PA port to carry the RI signal for serial port 2. config ETRAX_SER2_DSR_ON_PA_BIT - int "Ser2 DSR on PA bit" - depends on ETRAX_SER2_DTR_RI_DSR_CD_ON_PA - default "6" + int "Ser2 DSR on PA bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT2 + default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED + default "6" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED help Specify the pin of the PA port to carry the DTR signal for serial port 2. config ETRAX_SER2_CD_ON_PA_BIT - int "Ser2 CD on PA bit" - depends on ETRAX_SER2_DTR_RI_DSR_CD_ON_PA - default "7" + int "Ser2 CD on PA bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT2 + default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PA && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED + default "7" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PA || ETRAX_SER2_DTR_RI_DSR_CD_MIXED help Specify the pin of the PA port to carry the CD signal for serial port 2. +config ETRAX_SER2_DTR_ON_PB_BIT + int "Ser2 DTR on PB bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT2 + default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED + default "4" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED + +config ETRAX_SER2_RI_ON_PB_BIT + int "Ser2 RI on PB bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT2 + default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED + default "5" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED + +config ETRAX_SER2_DSR_ON_PB_BIT + int "Ser2 DSR on PB bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT2 + default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED + default "6" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED + +config ETRAX_SER2_CD_ON_PB_BIT + int "Ser2 CD on PB bit (-1 = not used)" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT2 + default "-1" if !ETRAX_SER2_DTR_RI_DSR_CD_ON_PB && !ETRAX_SER2_DTR_RI_DSR_CD_MIXED + default "7" if ETRAX_SER2_DTR_RI_DSR_CD_ON_PB || ETRAX_SER2_DTR_RI_DSR_CD_MIXED + config ETRAX_SERIAL_PORT3 bool "Serial port 3 enabled" depends on ETRAX_SERIAL help Enables the ETRAX 100 serial driver for ser3 (ttyS3). +choice + prompt "Ser3 DTR, RI, DSR and CD assignment" + depends on ETRAX_SERIAL_PORT3 + default ETRAX_SER3_DTR_RI_DSR_CD_ON_NONE + +config ETRAX_SER3_DTR_RI_DSR_CD_ON_NONE + bool "No_DTR_RI_DSR_CD" + +config ETRAX_SER3_DTR_RI_DSR_CD_ON_PA + bool "DTR_RI_DSR_CD_on_PA" + +config ETRAX_SER3_DTR_RI_DSR_CD_ON_PB + bool "DTR_RI_DSR_CD_on_PB" + +config ETRAX_SER3_DTR_RI_DSR_CD_MIXED + bool "DTR_RI_DSR_CD_mixed_on_PA_and_PB" + +endchoice + +config ETRAX_SER3_DTR_ON_PA_BIT + int "Ser3 DTR on PA bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PA || ETRAX_SER3_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT3 + default "-1" + +config ETRAX_SER3_RI_ON_PA_BIT + int "Ser3 RI on PA bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PA || ETRAX_SER3_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT3 + default "-1" + +config ETRAX_SER3_DSR_ON_PA_BIT + int "Ser3 DSR on PA bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PA || ETRAX_SER3_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT3 + default "-1" + +config ETRAX_SER3_CD_ON_PA_BIT + int "Ser3 CD on PA bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PA || ETRAX_SER3_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT3 + default "-1" + +config ETRAX_SER3_DTR_ON_PB_BIT + int "Ser3 DTR on PB bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PB || ETRAX_SER3_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT3 + default "-1" + +config ETRAX_SER3_RI_ON_PB_BIT + int "Ser3 RI on PB bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PB || ETRAX_SER3_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT3 + default "-1" + +config ETRAX_SER3_DSR_ON_PB_BIT + int "Ser3 DSR on PB bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PB || ETRAX_SER3_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT3 + default "-1" + +config ETRAX_SER3_CD_ON_PB_BIT + int "Ser3 CD on PB bit (-1 = not used)" if ETRAX_SER3_DTR_RI_DSR_CD_ON_PB || ETRAX_SER3_DTR_RI_DSR_CD_MIXED + depends on ETRAX_SERIAL_PORT3 + default "-1" + config ETRAX_RS485 bool "RS-485 support" depends on ETRAX_SERIAL @@ -272,7 +446,7 @@ config ETRAX_RS485_ON_PA bool "RS-485 mode on PA" depends on ETRAX_RS485 help - Control Driver Output Enable on RS485 tranceiver using a pin on PA + Control Driver Output Enable on RS485 transceiver using a pin on PA port: Axis 2400/2401 uses PA 3. @@ -281,7 +455,7 @@ config ETRAX_RS485_ON_PA_BIT depends on ETRAX_RS485_ON_PA default "3" help - Control Driver Output Enable on RS485 tranceiver using a this bit + Control Driver Output Enable on RS485 transceiver using a this bit on PA port. config ETRAX_RS485_DISABLE_RECEIVER @@ -292,299 +466,9 @@ config ETRAX_RS485_DISABLE_RECEIVER loopback. Not all products are able to do this in software only. Axis 2400/2401 must disable receiver. -config ETRAX_SYNCHRONOUS_SERIAL - bool "Synchronous serial port support" - help - This option enables support for the ETRAX 100LX built-in - synchronous serial ports. These ports are used for continuous - streamed data like audio. The default setting is compatible - with the STA 013 MP3 decoder, but can easily be tuned to fit - any other audio encoder/decoder and SPI. - -config ETRAX_SYNCHRONOUS_SERIAL_PORT0 - bool "Synchronous serial port 0 enabled" - depends on ETRAX_SYNCHRONOUS_SERIAL - help - Enables the ETRAX 100LX synchronous serial port 0 (syncser0). - -config ETRAX_SYNCHRONOUS_SERIAL0_DMA - bool "Synchronous serial port 0 uses DMA" - depends on ETRAX_SYNCHRONOUS_SERIAL_PORT0 - help - Makes synchronous serial port 0 use DMA. - -config ETRAX_SYNCHRONOUS_SERIAL_PORT1 - bool "Synchronous serial port 1 enabled" - depends on ETRAX_SYNCHRONOUS_SERIAL - help - Enables the ETRAX 100LX synchronous serial port 1 (syncser1). - -config ETRAX_SYNCHRONOUS_SERIAL1_DMA - bool "Synchronous serial port 1 uses DMA" - depends on ETRAX_SYNCHRONOUS_SERIAL_PORT1 - help - Makes synchronous serial port 1 use DMA. - -config ETRAX_PARPORT - bool "Parallel port support" - help - Say Y here to enable the ETRAX on-board parallel ports. - -config ETRAX_PARALLEL_PORT0 - bool "Parallel port 0 enabled" - depends on ETRAX_PARPORT - help - Say Y here to enable parallel port 0. - -config ETRAX_PARALLEL_PORT1 - bool "Parallel port 1 enabled" - depends on ETRAX_PARPORT - help - Say Y here to enable parallel port 1. - -# here we define the CONFIG_'s necessary to enable parallel port support -config PARPORT - tristate - depends on ETRAX_PARPORT - default y - ---help--- - If you want to use devices connected to your machine's parallel port - (the connector at the computer with 25 holes), e.g. printer, ZIP - drive, PLIP link (Parallel Line Internet Protocol is mainly used to - create a mini network by connecting the parallel ports of two local - machines) etc., then you need to say Y here; please read - <file:Documentation/parport.txt> and - <file:drivers/parport/BUGS-parport>. - - For extensive information about drivers for many devices attaching - to the parallel port see <http://www.torque.net/linux-pp.html> on - the WWW. - - It is possible to share a single parallel port among several devices - and it is safe to compile all the corresponding drivers into the - kernel. If you want to compile parallel port support as a module - ( = code which can be inserted in and removed from the running - kernel whenever you want), say M here and read - <file:Documentation/modules.txt>. The module will be called - parport. If you have more than one parallel port and want to - specify which port and IRQ to be used by this driver at module load - time, take a look at <file:Documentation/parport.txt>. - - If unsure, say Y. - -config PARPORT_1284 - bool - depends on ETRAX_PARPORT - default y - help - If you have a printer that supports status readback or device ID, or - want to use a device that uses enhanced parallel port transfer modes - such as EPP and ECP, say Y here to enable advanced IEEE 1284 - transfer modes. Also say Y if you want device ID information to - appear in /proc/sys/dev/parport/*/autoprobe*. It is safe to say N. - -config PRINTER - tristate - depends on ETRAX_PARPORT - default y - ---help--- - If you intend to attach a printer to the parallel port of your Linux - box (as opposed to using a serial printer; if the connector at the - printer has 9 or 25 holes ["female"], then it's serial), say Y. - Also read the Printing-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. - - It is possible to share one parallel port among several devices - (e.g. printer and ZIP drive) and it is safe to compile the - corresponding drivers into the kernel. If you want to compile this - driver as a module however ( = code which can be inserted in and - removed from the running kernel whenever you want), say M here and - read <file:Documentation/modules.txt> and - <file:Documentation/parport.txt>. The module will be called lp. - - If you have several parallel ports, you can specify which ports to - use with the "lp" kernel command line option. (Try "man bootparam" - or see the documentation of your boot loader (lilo or loadlin) about - how to pass options to the kernel at boot time.) The syntax of the - "lp" command line option can be found in <file:drivers/char/lp.c>. - - If you have more than 8 printers, you need to increase the LP_NO - macro in lp.c and the PARPORT_MAX macro in parport.h. - -config ETRAX_IDE - bool "ATA/IDE support" - help - Enable this to get support for ATA/IDE. You can't use parallel - ports or SCSI ports at the same time. - -# here we should add the CONFIG_'s necessary to enable the basic -# general ide drivers so the common case does not need to go -# into that config submenu. enable disk and CD support. others -# need to go fiddle in the submenu.. -config IDE - tristate - depends on ETRAX_IDE - default y - ---help--- - If you say Y here, your kernel will be able to manage low cost mass - storage units such as ATA/(E)IDE and ATAPI units. The most common - cases are IDE hard drives and ATAPI CD-ROM drives. - - If your system is pure SCSI and doesn't use these interfaces, you - can say N here. - - Integrated Disk Electronics (IDE aka ATA-1) is a connecting standard - for mass storage units such as hard disks. It was designed by - Western Digital and Compaq Computer in 1984. It was then named - ST506. Quite a number of disks use the IDE interface. - - AT Attachment (ATA) is the superset of the IDE specifications. - ST506 was also called ATA-1. - - Fast-IDE is ATA-2 (also named Fast ATA), Enhanced IDE (EIDE) is - ATA-3. It provides support for larger disks (up to 8.4GB by means of - the LBA standard), more disks (4 instead of 2) and for other mass - storage units such as tapes and cdrom. UDMA/33 (aka UltraDMA/33) is - ATA-4 and provides faster (and more CPU friendly) transfer modes - than previous PIO (Programmed processor Input/Output) from previous - ATA/IDE standards by means of fast DMA controllers. - - ATA Packet Interface (ATAPI) is a protocol used by EIDE tape and - CD-ROM drives, similar in many respects to the SCSI protocol. - - SMART IDE (Self Monitoring, Analysis and Reporting Technology) was - designed in order to prevent data corruption and disk crash by - detecting pre hardware failure conditions (heat, access time, and - the like...). Disks built since June 1995 may follow this standard. - The kernel itself don't manage this; however there are quite a - number of user programs such as smart that can query the status of - SMART parameters disk. - - If you want to compile this driver as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read <file:Documentation/modules.txt>. The module - will be called ide. - - For further information, please read <file:Documentation/ide.txt>. - - If unsure, say Y. - -config BLK_DEV_IDE - tristate - depends on ETRAX_IDE - default y - ---help--- - If you say Y here, you will use the full-featured IDE driver to - control up to ten ATA/IDE interfaces, each being able to serve a - "master" and a "slave" device, for a total of up to twenty ATA/IDE - disk/cdrom/tape/floppy drives. - - Useful information about large (>540 MB) IDE disks, multiple - interfaces, what to do if ATA/IDE devices are not automatically - detected, sound card ATA/IDE ports, module support, and other - topics, is contained in <file:Documentation/ide.txt>. For detailed - information about hard drives, consult the Disk-HOWTO and the - Multi-Disk-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. - - To fine-tune ATA/IDE drive/interface parameters for improved - performance, look for the hdparm package at - <ftp://ibiblio.org/pub/Linux/system/hardware/>. - - If you want to compile this driver as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read <file:Documentation/modules.txt> and - <file:Documentation/ide.txt>. The module will be called ide-mod. - Do not compile this driver as a module if your root file system (the - one containing the directory /) is located on an IDE device. - - If you have one or more IDE drives, say Y or M here. If your system - has no IDE drives, or if memory requirements are really tight, you - could say N here, and select the "Old hard disk driver" below - instead to save about 13 KB of memory in the kernel. - -config BLK_DEV_IDEDISK - tristate - depends on ETRAX_IDE - default y - ---help--- - This will include enhanced support for MFM/RLL/IDE hard disks. If - you have a MFM/RLL/IDE disk, and there is no special reason to use - the old hard disk driver instead, say Y. If you have an SCSI-only - system, you can say N here. - - If you want to compile this driver as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read <file:Documentation/modules.txt>. The module - will be called ide-disk. Do not compile this driver as a module - if your root file system (the one containing the directory /) is - located on the IDE disk. If unsure, say Y. - -config BLK_DEV_IDECD - tristate - depends on ETRAX_IDE - default y - ---help--- - If you have a CD-ROM drive using the ATAPI protocol, say Y. ATAPI is - a newer protocol used by IDE CD-ROM and TAPE drives, similar to the - SCSI protocol. Most new CD-ROM drives use ATAPI, including the - NEC-260, Mitsumi FX400, Sony 55E, and just about all non-SCSI - double(2X) or better speed drives. - - If you say Y here, the CD-ROM drive will be identified at boot time - along with other IDE devices, as "hdb" or "hdc", or something - similar (check the boot messages with dmesg). If this is your only - CD-ROM drive, you can say N to all other CD-ROM options, but be sure - to say Y or M to "ISO 9660 CD-ROM file system support". - - Note that older versions of LILO (LInux LOader) cannot properly deal - with IDE/ATAPI CD-ROMs, so install LILO 16 or higher, available from - <ftp://brun.dyndns.org/pub/linux/lilo/>. - - If you want to compile the driver as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read <file:Documentation/modules.txt>. The module - will be called ide-cd. - -config BLK_DEV_IDEDMA - bool - depends on ETRAX_IDE - default y - -config ETRAX_IDE_DELAY - int "Delay for drives to regain consciousness" - depends on ETRAX_IDE - default "15" - help - Sets the time to wait for disks to regain consciousness after reset. - -choice - prompt "IDE reset pin" - depends on ETRAX_IDE - default ETRAX_IDE_PB7_RESET - -config ETRAX_IDE_PB7_RESET - bool "Port_PB_Bit_7" - help - Configures the pin used to reset the IDE bus. - -config ETRAX_IDE_G27_RESET - bool "Port_G_Bit_27" - help - Configures the pin used to reset the IDE bus. - -config ETRAX_IDE_CSE1_16_RESET - bool "Port_CSE1_Bit_16" - -config ETRAX_IDE_CSP0_8_RESET - bool "Port_CSP0_Bit_08" - help - Configures the pin used to reset the IDE bus. - -endchoice - config ETRAX_AXISFLASHMAP bool "Axis flash-map support" + depends on ETRAX_ARCH_V10 help This option enables MTD mapping of flash devices. Needed to use flash memories. If unsure, say Y. @@ -637,6 +521,19 @@ config MTD_CFI_AMDSTD provides support for one of those command sets, used on chips chips including the AMD Am29LV320. +config MTD_OBSOLETE_CHIPS + bool + depends on ETRAX_AXISFLASHMAP + default y + help + This option does not enable any code directly, but will allow you to + select some other chip drivers which are now considered obsolete, + because the generic CONFIG_JEDEC_PROBE code above should now detect + the chips which are supported by these drivers, and allow the generic + CFI-compatible drivers to drive the chips. Say 'N' here unless you have + already tried the CONFIG_JEDEC_PROBE method and reported its failure + to the MTD mailing list at <linux-mtd@lists.infradead.org> + config MTD_AMDSTD tristate depends on ETRAX_AXISFLASHMAP @@ -695,10 +592,16 @@ config MTD_PARTITIONS devices. Partitioning on NFTL 'devices' is a different - that's the 'normal' form of partitioning used on a block device. +config MTD_CONCAT + tristate + depends on ETRAX_AXISFLASHMAP + default y + config ETRAX_I2C bool "I2C support" + depends on ETRAX_ARCH_V10 help - Enables an I2C driver on PB0 and PB1 on ETRAX100. + Enables an I2C driver on ETRAX100. EXAMPLE usage: i2c_arg = I2C_WRITEARG(STA013_WRITE_ADDR, reg, val); ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_WRITEREG), i2c_arg); @@ -717,8 +620,23 @@ config ETRAX_I2C_USES_PB_NOT_PB_I2C I2C driver, like the DS1302 realtime-clock driver. If you are uncertain, choose Y here. +config ETRAX_I2C_DATA_PORT + int "I2C SDA bit number" + depends on ETRAX_I2C_USES_PB_NOT_PB_I2C + default "0" + help + Selects the pin on Port B where the data pin is connected + +config ETRAX_I2C_CLK_PORT + int "I2C SCL bit number" + depends on ETRAX_I2C_USES_PB_NOT_PB_I2C + default "1" + help + Select the pin on Port B where the clock pin is connected + config ETRAX_I2C_EEPROM bool "I2C EEPROM (non-volatile RAM) support" + depends on ETRAX_I2C help Enables I2C EEPROM (non-volatile RAM) on PB0 and PB1 using the I2C driver. Select size option: Probed, 2k, 8k, 16k. @@ -755,8 +673,9 @@ endchoice config ETRAX_GPIO bool "GPIO support" + depends on ETRAX_ARCH_V10 ---help--- - Enables the Etrax general port device (major 120, minors 0 and 1). + Enables the ETRAX general port device (major 120, minors 0 and 1). You can use this driver to access the general port bits. It supports these ioctl's: #include <linux/etraxgpio.h> @@ -778,7 +697,7 @@ config ETRAX_PA_BUTTON_BITMASK use 02 here. Use 00 if there are no buttons on PA. If the bitmask is <> 00 a button driver will be included in the gpio - driver. Etrax general I/O support must be enabled. + driver. ETRAX general I/O support must be enabled. config ETRAX_PA_CHANGEABLE_DIR hex "PA user changeable dir mask" @@ -820,71 +739,37 @@ config ETRAX_PB_CHANGEABLE_BITS Bit set = changeable. You probably want 00 here. -#bool 'ARTPEC-1 support' CONFIG_JULIETTE -# -#if [ "$CONFIG_JULIETTE" = "y" ]; then -# source arch/cris/drivers/juliette/Config.in -#fi -config ETRAX_USB_HOST - bool "USB host" +config ETRAX_RTC + bool "Real Time Clock support" + depends on ETRAX_ARCH_V10 help - This option enables the host functionality of the ETRAX 100LX - built-in USB controller. In host mode the controller is designed - for CTRL and BULK traffic only, INTR traffic may work as well - however (depending on the requirements of timeliness). - -config USB - tristate - depends on ETRAX_USB_HOST - default y - ---help--- - Universal Serial Bus (USB) is a specification for a serial bus - subsystem which offers higher speeds and more features than the - traditional PC serial port. The bus supplies power to peripherals - and allows for hot swapping. Up to 127 USB peripherals can be - connected to a single USB port in a tree structure. The USB port is - the root of the tree, the peripherals are the leaves and the inner - nodes are special USB devices called hubs. Many newer PC's have USB - ports and newer peripherals such as scanners, keyboards, mice, - modems, and printers support the USB protocol and can be connected - to the PC via those ports. - - Say Y here if your computer has a USB port and you want to use USB - devices. You then need to say Y to at least one of "UHCI support" - or "OHCI support" below (the type of interface that the USB hardware - in your computer provides to the operating system) and then choose - from among the drivers for USB peripherals. You may want to check - out the information provided in <file:Documentation/usb/> and - especially the links given in <file:Documentation/usb/usb-help.txt>. - - This code is also available as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called usbcore. If you want to compile it as a - module, say M here and read <file:Documentation/modules.txt>. - -config ETRAX_USB_HOST_PORT1 - bool "USB port 1 enabled" - depends on ETRAX_USB_HOST - help - This option enables port 1 of the ETRAX 100LX USB root hub (RH). - -config ETRAX_USB_HOST_PORT2 - bool "USB port 2 enabled" - depends on ETRAX_USB_HOST - help - This option enables port 2 of the ETRAX 100LX USB root hub (RH). - -config ETRAX_DS1302 - bool "DS1302 Real Time Clock support" - help - Enables the driver for the DS1302 Real-Time Clock battery-backed - chip on some products. The kernel reads the time when booting, and + Enables drivers for the Real-Time Clock battery-backed chips on + some products. The kernel reads the time when booting, and the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a rtc_time struct (see <file:include/asm-cris/rtc.h>) on the /dev/rtc device, major 121. You can check the time with cat /proc/rtc, but normal time reading should be done using libc function time and friends. +choice + prompt "RTC chip" + depends on ETRAX_RTC + default ETRAX_DS1302 + +config ETRAX_DS1302 + bool "DS1302" + help + Enables the driver for the DS1302 Real-Time Clock battery-backed + chip on some products. + +config ETRAX_PCF8563 + bool "PCF8563" + help + Enables the driver for the PCF8563 Real-Time Clock battery-backed + chip on some products. + +endchoice + config ETRAX_DS1302_RST_ON_GENERIC_PORT bool "DS1302 RST on Generic Port" depends on ETRAX_DS1302 @@ -919,5 +804,17 @@ config ETRAX_DS1302_SDABIT This is the bit number for the SDA signal line of the DS1302 RTC on Port PB. This is probably best left at 2. -endmenu +config ETRAX_DS1302_TRICKLE_CHARGE + int "DS1302 Trickle charger value" + depends on ETRAX_DS1302 + default "0" + help + This controls the initial value of the trickle charge register. + 0 = disabled (use this if you are unsure or have a non rechargable battery) + Otherwise the following values can be OR:ed together to control the + charge current: + 1 = 2kohm, 2 = 4kohm, 3 = 4kohm + 4 = 1 diode, 8 = 2 diodes + Allowed values are (increasing current): 0, 11, 10, 9, 7, 6, 5 + diff --git a/arch/cris/drivers/Makefile b/arch/cris/arch-v10/drivers/Makefile index 50da6d18d529..7160cdd7bdd1 100644 --- a/arch/cris/drivers/Makefile +++ b/arch/cris/arch-v10/drivers/Makefile @@ -4,13 +4,11 @@ obj-$(CONFIG_ETRAX_ETHERNET) += ethernet.o obj-$(CONFIG_ETRAX_SERIAL) += serial.o -obj-$(CONFIG_ETRAX_IDE) += ide.o obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o obj-$(CONFIG_ETRAX_I2C) += i2c.o obj-$(CONFIG_ETRAX_I2C_EEPROM) += eeprom.o obj-$(CONFIG_ETRAX_GPIO) += gpio.o -obj-$(CONFIG_ETRAX_USB_HOST) += usb-host.o -obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o -obj-$(CONFIG_ETRAX_PARPORT) += parport.o obj-$(CONFIG_ETRAX_DS1302) += ds1302.o -obj-$(CONFIG_ETRAX_ETHERNET_LPSLAVE) += lpslave +obj-$(CONFIG_ETRAX_PCF8563) += pcf8563.o + + diff --git a/arch/cris/arch-v10/drivers/axisflashmap.c b/arch/cris/arch-v10/drivers/axisflashmap.c new file mode 100644 index 000000000000..c6f90bdcf07c --- /dev/null +++ b/arch/cris/arch-v10/drivers/axisflashmap.c @@ -0,0 +1,541 @@ +/* + * Physical mapping layer for MTD using the Axis partitiontable format + * + * Copyright (c) 2001, 2002 Axis Communications AB + * + * This file is under the GPL. + * + * First partition is always sector 0 regardless of if we find a partitiontable + * or not. In the start of the next sector, there can be a partitiontable that + * tells us what other partitions to define. If there isn't, we use a default + * partition split defined below. + * + * $Log: axisflashmap.c,v $ + * Revision 1.6 2003/07/04 08:27:37 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.5 2002/12/11 13:13:57 starvik + * Added arch/ to v10 specific includes + * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) + * + * Revision 1.4 2002/11/20 11:56:10 starvik + * Merge of Linux 2.5.48 + * + * Revision 1.3 2002/11/13 14:54:13 starvik + * Copied from linux 2.4 + * + * Revision 1.28 2002/10/01 08:08:43 jonashg + * The first partition ends at the start of the partition table. + * + * Revision 1.27 2002/08/21 09:23:13 jonashg + * Speling. + * + * Revision 1.26 2002/08/21 08:35:20 jonashg + * Cosmetic change to printouts. + * + * Revision 1.25 2002/08/21 08:15:42 jonashg + * Made it compile even without CONFIG_MTD_CONCAT defined. + * + * Revision 1.24 2002/08/20 13:12:35 jonashg + * * New approach to probing. Probe cse0 and cse1 separately and (mtd)concat + * the results. + * * Removed compile time tests concerning how the mtdram driver has been + * configured. The user will know about the misconfiguration at runtime + * instead. (The old approach made it impossible to use mtdram for anything + * else than RAM boot). + * + * Revision 1.23 2002/05/13 12:12:28 johana + * Allow compile without CONFIG_MTD_MTDRAM but warn at compiletime and + * be informative at runtime. + * + * Revision 1.22 2002/05/13 10:24:44 johana + * Added #if checks on MTDRAM CONFIG + * + * Revision 1.21 2002/05/06 16:05:20 johana + * Removed debug printout. + * + * Revision 1.20 2002/05/06 16:03:00 johana + * No more cramfs as root hack in generic code. + * It's handled by axisflashmap using mtdram. + * + * Revision 1.19 2002/03/15 17:10:28 bjornw + * Changed comment about cached access since we changed this before + * + * Revision 1.18 2002/03/05 17:06:15 jonashg + * Try amd_flash probe before cfi_probe since amd_flash driver can handle two + * (or more) flash chips of different model and the cfi driver cannot. + * + * Revision 1.17 2001/11/12 19:42:38 pkj + * Fixed compiler warnings. + * + * Revision 1.16 2001/11/08 11:18:58 jonashg + * Always read from uncached address to avoid problems with flushing + * cachelines after write and MTD-erase. No performance loss have been + * seen yet. + * + * Revision 1.15 2001/10/19 12:41:04 jonashg + * Name of probe has changed in MTD. + * + * Revision 1.14 2001/09/21 07:14:10 jonashg + * Made root filesystem (cramfs) use mtdblock driver when booting from flash. + * + * Revision 1.13 2001/08/15 13:57:35 jonashg + * Entire MTD updated to the linux 2.4.7 version. + * + * Revision 1.12 2001/06/11 09:50:30 jonashg + * Oops, 2MB is 0x200000 bytes. + * + * Revision 1.11 2001/06/08 11:39:44 jonashg + * Changed sizes and offsets in axis_default_partitions to use + * CONFIG_ETRAX_PTABLE_SECTOR. + * + * Revision 1.10 2001/05/29 09:42:03 jonashg + * Use macro for end marker length instead of sizeof. + * + * Revision 1.9 2001/05/29 08:52:52 jonashg + * Gave names to the magic fours (size of the ptable end marker). + * + * Revision 1.8 2001/05/28 15:36:20 jonashg + * * Removed old comment about ptable location in flash (it's a CONFIG_ option). + * * Variable ptable was initialized twice to the same value. + * + * Revision 1.7 2001/04/05 13:41:46 markusl + * Updated according to review remarks + * + * Revision 1.6 2001/03/07 09:21:21 bjornw + * No need to waste .data + * + * Revision 1.5 2001/03/06 16:27:01 jonashg + * Probe the entire flash area for flash devices. + * + * Revision 1.4 2001/02/23 12:47:15 bjornw + * Uncached flash in LOW_MAP moved from 0xe to 0x8 + * + * Revision 1.3 2001/02/16 12:11:45 jonashg + * MTD driver amd_flash is now included in MTD CVS repository. + * (It's now in drivers/mtd). + * + * Revision 1.2 2001/02/09 11:12:22 jonashg + * Support for AMD compatible non-CFI flash chips. + * Only tested with Toshiba TC58FVT160 so far. + * + * Revision 1.1 2001/01/12 17:01:18 bjornw + * * Added axisflashmap.c, a physical mapping for MTD that reads and understands + * Axis partition-table format. + * + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/config.h> +#include <linux/init.h> + +#include <linux/mtd/concat.h> +#include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/mtdram.h> +#include <linux/mtd/partitions.h> + +#include <asm/axisflashmap.h> +#include <asm/mmu.h> +#include <asm/arch/sv_addr_ag.h> + +#ifdef CONFIG_CRIS_LOW_MAP +#define FLASH_UNCACHED_ADDR KSEG_8 +#define FLASH_CACHED_ADDR KSEG_5 +#else +#define FLASH_UNCACHED_ADDR KSEG_E +#define FLASH_CACHED_ADDR KSEG_F +#endif + +/* From head.S */ +extern unsigned long romfs_start, romfs_length, romfs_in_flash; + +/* Map driver functions. */ + +static __u8 flash_read8(struct map_info *map, unsigned long ofs) +{ + return *(__u8 *)(map->map_priv_1 + ofs); +} + +static __u16 flash_read16(struct map_info *map, unsigned long ofs) +{ + return *(__u16 *)(map->map_priv_1 + ofs); +} + +static __u32 flash_read32(struct map_info *map, unsigned long ofs) +{ + return *(volatile unsigned int *)(map->map_priv_1 + ofs); +} + +static void flash_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + memcpy(to, (void *)(map->map_priv_1 + from), len); +} + +static void flash_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + *(__u8 *)(map->map_priv_1 + adr) = d; +} + +static void flash_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + *(__u16 *)(map->map_priv_1 + adr) = d; +} + +static void flash_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + *(__u32 *)(map->map_priv_1 + adr) = d; +} + +/* + * The map for chip select e0. + * + * We run into tricky coherence situations if we mix cached with uncached + * accesses to we only use the uncached version here. + * + * The size field is the total size where the flash chips may be mapped on the + * chip select. MTD probes should find all devices there and it does not matter + * if there are unmapped gaps or aliases (mirrors of flash devices). The MTD + * probes will ignore them. + * + * The start address in map_priv_1 is in virtual memory so we cannot use + * MEM_CSE0_START but must rely on that FLASH_UNCACHED_ADDR is the start + * address of cse0. + */ +static struct map_info map_cse0 = { + .name = "cse0", + .size = MEM_CSE0_SIZE, + .buswidth = CONFIG_ETRAX_FLASH_BUSWIDTH, + .read8 = flash_read8, + .read16 = flash_read16, + .read32 = flash_read32, + .copy_from = flash_copy_from, + .write8 = flash_write8, + .write16 = flash_write16, + .write32 = flash_write32, + .map_priv_1 = FLASH_UNCACHED_ADDR +}; + +/* + * The map for chip select e1. + * + * If there was a gap between cse0 and cse1, map_priv_1 would get the wrong + * address, but there isn't. + */ +static struct map_info map_cse1 = { + .name = "cse1", + .size = MEM_CSE1_SIZE, + .buswidth = CONFIG_ETRAX_FLASH_BUSWIDTH, + .read8 = flash_read8, + .read16 = flash_read16, + .read32 = flash_read32, + .copy_from = flash_copy_from, + .write8 = flash_write8, + .write16 = flash_write16, + .write32 = flash_write32, + .map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE +}; + +/* If no partition-table was found, we use this default-set. */ +#define MAX_PARTITIONS 7 +#define NUM_DEFAULT_PARTITIONS 3 + +/* + * Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the + * size of one flash block and "filesystem"-partition needs 5 blocks to be able + * to use JFFS. + */ +static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { + { + .name = "boot firmware", + .size = CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0 + }, + { + .name = "kernel", + .size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR), + .offset = CONFIG_ETRAX_PTABLE_SECTOR + }, + { + .name = "filesystem", + .size = 5 * CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR) + } +}; + +/* Initialize the ones normally used. */ +static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { + { + .name = "part0", + .size = CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0 + }, + { + .name = "part1", + .size = 0, + .offset = 0 + }, + { + .name = "part2", + .size = 0, + .offset = 0 + }, + { + .name = "part3", + .size = 0, + .offset = 0 + }, + { + .name = "part4", + .size = 0, + .offset = 0 + }, + { + .name = "part5", + .size = 0, + .offset = 0 + }, + { + .name = "part6", + .size = 0, + .offset = 0 + }, +}; + +/* + * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash + * chips in that order (because the amd_flash-driver is faster). + */ +static struct mtd_info *probe_cs(struct map_info *map_cs) +{ + struct mtd_info *mtd_cs = NULL; + + printk("%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n", + map_cs->name, map_cs->size, map_cs->map_priv_1); + +#ifdef CONFIG_MTD_AMDSTD + mtd_cs = do_map_probe("amd_flash", map_cs); +#endif +#ifdef CONFIG_MTD_CFI + if (!mtd_cs) { + mtd_cs = do_map_probe("cfi_probe", map_cs); + } +#endif + + return mtd_cs; +} + +/* + * Probe each chip select individually for flash chips. If there are chips on + * both cse0 and cse1, the mtd_info structs will be concatenated to one struct + * so that MTD partitions can cross chip boundries. + * + * The only known restriction to how you can mount your chips is that each + * chip select must hold similar flash chips. But you need external hardware + * to do that anyway and you can put totally different chips on cse0 and cse1 + * so it isn't really much of a restriction. + */ +static struct mtd_info *flash_probe(void) +{ + struct mtd_info *mtd_cse0; + struct mtd_info *mtd_cse1; + struct mtd_info *mtd_cse; + + mtd_cse0 = probe_cs(&map_cse0); + mtd_cse1 = probe_cs(&map_cse1); + + if (!mtd_cse0 && !mtd_cse1) { + /* No chip found. */ + return NULL; + } + + if (mtd_cse0 && mtd_cse1) { +#ifdef CONFIG_MTD_CONCAT + struct mtd_info *mtds[] = { mtd_cse0, mtd_cse1 }; + + /* Since the concatenation layer adds a small overhead we + * could try to figure out if the chips in cse0 and cse1 are + * identical and reprobe the whole cse0+cse1 window. But since + * flash chips are slow, the overhead is relatively small. + * So we use the MTD concatenation layer instead of further + * complicating the probing procedure. + */ + mtd_cse = mtd_concat_create(mtds, + sizeof(mtds) / sizeof(mtds[0]), + "cse0+cse1"); +#else + printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel " + "(mis)configuration!\n", map_cse0.name, map_cse1.name); + mtd_cse = NULL; +#endif + if (!mtd_cse) { + printk(KERN_ERR "%s and %s: Concatenation failed!\n", + map_cse0.name, map_cse1.name); + + /* The best we can do now is to only use what we found + * at cse0. + */ + mtd_cse = mtd_cse0; + map_destroy(mtd_cse1); + } + } else { + mtd_cse = mtd_cse0? mtd_cse0 : mtd_cse1; + } + + return mtd_cse; +} + +/* + * Probe the flash chip(s) and, if it succeeds, read the partition-table + * and register the partitions with MTD. + */ +static int __init init_axis_flash(void) +{ + struct mtd_info *mymtd; + int err = 0; + int pidx = 0; + struct partitiontable_head *ptable_head; + struct partitiontable_entry *ptable; + int use_default_ptable = 1; /* Until proven otherwise. */ + const char *pmsg = " /dev/flash%d at 0x%08x, size 0x%08x\n"; + + if (!(mymtd = flash_probe())) { + /* There's no reason to use this module if no flash chip can + * be identified. Make sure that's understood. + */ + panic("axisflashmap found no flash chip!\n"); + } + + printk("%s: 0x%08x bytes of flash memory.\n", + mymtd->name, mymtd->size); + + mymtd->owner = THIS_MODULE; + + ptable_head = (struct partitiontable_head *)(FLASH_CACHED_ADDR + + CONFIG_ETRAX_PTABLE_SECTOR + PARTITION_TABLE_OFFSET); + pidx++; /* First partition is always set to the default. */ + + if ((ptable_head->magic == PARTITION_TABLE_MAGIC) + && (ptable_head->size < + (MAX_PARTITIONS * sizeof(struct partitiontable_entry) + + PARTITIONTABLE_END_MARKER_SIZE)) + && (*(unsigned long*)((void*)ptable_head + sizeof(*ptable_head) + + ptable_head->size - + PARTITIONTABLE_END_MARKER_SIZE) + == PARTITIONTABLE_END_MARKER)) { + /* Looks like a start, sane length and end of a + * partition table, lets check csum etc. + */ + int ptable_ok = 0; + struct partitiontable_entry *max_addr = + (struct partitiontable_entry *) + ((unsigned long)ptable_head + sizeof(*ptable_head) + + ptable_head->size); + unsigned long offset = CONFIG_ETRAX_PTABLE_SECTOR; + unsigned char *p; + unsigned long csum = 0; + + ptable = (struct partitiontable_entry *) + ((unsigned long)ptable_head + sizeof(*ptable_head)); + + /* Lets be PARANOID, and check the checksum. */ + p = (unsigned char*) ptable; + + while (p <= (unsigned char*)max_addr) { + csum += *p++; + csum += *p++; + csum += *p++; + csum += *p++; + } + ptable_ok = (csum == ptable_head->checksum); + + /* Read the entries and use/show the info. */ + printk(" Found a%s partition table at 0x%p-0x%p.\n", + (ptable_ok ? " valid" : "n invalid"), ptable_head, + max_addr); + + /* We have found a working bootblock. Now read the + * partition table. Scan the table. It ends when + * there is 0xffffffff, that is, empty flash. + */ + while (ptable_ok + && ptable->offset != 0xffffffff + && ptable < max_addr + && pidx < MAX_PARTITIONS) { + + axis_partitions[pidx].offset = offset + ptable->offset; + axis_partitions[pidx].size = ptable->size; + + printk(pmsg, pidx, axis_partitions[pidx].offset, + axis_partitions[pidx].size); + pidx++; + ptable++; + } + use_default_ptable = !ptable_ok; + } + + if (romfs_in_flash) { + /* Add an overlapping device for the root partition (romfs). */ + + axis_partitions[pidx].name = "romfs"; + axis_partitions[pidx].size = romfs_length; + axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; + axis_partitions[pidx].mask_flags |= MTD_WRITEABLE; + + printk(" 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"); + err = add_mtd_partitions(mymtd, axis_default_partitions, + NUM_DEFAULT_PARTITIONS); + } else { + err = add_mtd_partitions(mymtd, axis_partitions, pidx); + } + + if (err) { + panic("axisflashmap could not add MTD partitions!\n"); + } + + if (!romfs_in_flash) { + /* Create an RAM device for the root partition (romfs). */ + +#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0) + /* No use trying to boot this kernel from RAM. Panic! */ + printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM " + "device due to kernel (mis)configuration!\n"); + panic("This kernel cannot boot from RAM!\n"); +#else + struct mtd_info *mtd_ram; + + mtd_ram = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), + GFP_KERNEL); + if (!mtd_ram) { + panic("axisflashmap couldn't allocate memory for " + "mtd_info!\n"); + } + + printk(" Adding RAM partition for romfs image:\n"); + printk(pmsg, pidx, romfs_start, romfs_length); + + err = mtdram_init_device(mtd_ram, (void*)romfs_start, + romfs_length, "romfs"); + if (err) { + panic("axisflashmap could not initialize MTD RAM " + "device!\n"); + } +#endif + } + + return err; +} + +/* This adds the above to the kernels init-call chain. */ +module_init(init_axis_flash); diff --git a/arch/cris/drivers/ds1302.c b/arch/cris/arch-v10/drivers/ds1302.c index 23814c3843d3..175062133724 100644 --- a/arch/cris/drivers/ds1302.c +++ b/arch/cris/arch-v10/drivers/ds1302.c @@ -7,8 +7,37 @@ *! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status *! *! $Log: ds1302.c,v $ -*! Revision 1.1.1.1 2001/12/17 13:59:27 bjornw -*! Import of Linux 2.5.1 +*! Revision 1.9 2003/07/04 08:27:37 starvik +*! Merge of Linux 2.5.74 +*! +*! Revision 1.8 2003/04/09 05:20:47 starvik +*! Merge of Linux 2.5.67 +*! +*! Revision 1.6 2003/01/09 14:42:51 starvik +*! Merge of Linux 2.5.55 +*! +*! Revision 1.4 2002/12/11 13:13:57 starvik +*! Added arch/ to v10 specific includes +*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) +*! +*! Revision 1.3 2002/11/20 11:56:10 starvik +*! Merge of Linux 2.5.48 +*! +*! Revision 1.2 2002/11/18 13:16:06 starvik +*! Linux 2.5 port of latest 2.4 drivers +*! +*! Revision 1.15 2002/10/11 16:14:33 johana +*! Added CONFIG_ETRAX_DS1302_TRICKLE_CHARGE and initial setting of the +*! trcklecharge register. +*! +*! Revision 1.14 2002/10/10 12:15:38 magnusmn +*! Added support for having the RST signal on bit g0 +*! +*! Revision 1.13 2002/05/29 15:16:08 johana +*! Removed unused variables. +*! +*! Revision 1.12 2002/04/10 15:35:25 johana +*! Moved probe function closer to init function and marked it __init. *! *! Revision 1.11 2001/06/14 12:35:52 jonashg *! The ATA hack is back. It is unfortunately the only way to set g27 to output. @@ -85,7 +114,7 @@ *! *! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN *! -*! $Id: ds1302.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +*! $Id: ds1302.c,v 1.9 2003/07/04 08:27:37 starvik Exp $ *! *!***************************************************************************/ @@ -101,7 +130,7 @@ #include <asm/uaccess.h> #include <asm/system.h> -#include <asm/svinto.h> +#include <asm/arch/svinto.h> #include <asm/io.h> #include <asm/rtc.h> @@ -232,54 +261,6 @@ ds1302_wdisable(void) stop(); } -/* Probe for the chip by writing something to its RAM and try reading it back. */ - -#define MAGIC_PATTERN 0x42 - -static int -ds1302_probe(void) -{ - int retval, res; - - TK_RST_DIR(1); - TK_SCL_DIR(1); - TK_SDA_DIR(0); - - /* Try to talk to timekeeper. */ - - ds1302_wenable(); - start(); - out_byte(0xc0); /* write RAM byte 0 */ - out_byte(MAGIC_PATTERN); /* write something magic */ - start(); - out_byte(0xc1); /* read RAM byte 0 */ - - if((res = in_byte()) == MAGIC_PATTERN) { - 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", - ds1302_name, - CONFIG_ETRAX_DS1302_SDABIT, - CONFIG_ETRAX_DS1302_SCLBIT, -#ifdef CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT - "GENIO", -#else - "PB", -#endif - CONFIG_ETRAX_DS1302_RSTBIT); - get_rtc_status(buf); - printk(buf); - retval = 1; - } else { - stop(); - printk("%s: RTC not found.\n", ds1302_name); - retval = 0; - } - - return retval; -} /* Read a byte from the selected register in the DS1302. */ @@ -315,8 +296,8 @@ get_rtc_time(struct rtc_time *rtc_tm) { unsigned long flags; - save_flags(flags); - cli(); + local_irq_save(flags); + local_irq_disable(); rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); @@ -325,7 +306,7 @@ get_rtc_time(struct rtc_time *rtc_tm) rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); rtc_tm->tm_year = CMOS_READ(RTC_YEAR); - restore_flags(flags); + local_irq_restore(flags); BCD_TO_BIN(rtc_tm->tm_sec); BCD_TO_BIN(rtc_tm->tm_min); @@ -371,7 +352,6 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, { struct rtc_time rtc_tm; unsigned char mon, day, hrs, min, sec, leap_yr; - unsigned char save_control, save_freq_select; unsigned int yrs; if (!capable(CAP_SYS_TIME)) @@ -414,15 +394,15 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, BIN_TO_BCD(mon); BIN_TO_BCD(yrs); - save_flags(flags); - cli(); + local_irq_save(flags); + local_irq_disable(); CMOS_WRITE(yrs, RTC_YEAR); CMOS_WRITE(mon, RTC_MONTH); CMOS_WRITE(day, RTC_DAY_OF_MONTH); CMOS_WRITE(hrs, RTC_HOURS); CMOS_WRITE(min, RTC_MINUTES); CMOS_WRITE(sec, RTC_SECONDS); - restore_flags(flags); + local_irq_restore(flags); /* Notice that at this point, the RTC is updated but * the kernel is still running with the old time. @@ -435,7 +415,6 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, case RTC_SET_CHARGE: /* set the RTC TRICKLE CHARGE register */ { int tcs_val; - unsigned char save_control, save_freq_select; if (!capable(CAP_SYS_TIME)) return -EPERM; @@ -484,6 +463,56 @@ static struct file_operations rtc_fops = { .ioctl = rtc_ioctl, }; +/* Probe for the chip by writing something to its RAM and try reading it back. */ + +#define MAGIC_PATTERN 0x42 + +static int __init +ds1302_probe(void) +{ + int retval, res; + + TK_RST_DIR(1); + TK_SCL_DIR(1); + TK_SDA_DIR(0); + + /* Try to talk to timekeeper. */ + + ds1302_wenable(); + start(); + out_byte(0xc0); /* write RAM byte 0 */ + out_byte(MAGIC_PATTERN); /* write something magic */ + start(); + out_byte(0xc1); /* read RAM byte 0 */ + + if((res = in_byte()) == MAGIC_PATTERN) { + 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", + ds1302_name, + CONFIG_ETRAX_DS1302_SDABIT, + CONFIG_ETRAX_DS1302_SCLBIT, +#ifdef CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT + "GENIO", +#else + "PB", +#endif + CONFIG_ETRAX_DS1302_RSTBIT); + get_rtc_status(buf); + printk(buf); + retval = 1; + } else { + stop(); + printk("%s: RTC not found.\n", ds1302_name); + retval = 0; + } + + return retval; +} + + /* Just probe for the RTC and register the device to handle the ioctl needed. */ int __init @@ -491,27 +520,47 @@ ds1302_init(void) { if (!ds1302_probe()) { #ifdef CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT +#if CONFIG_ETRAX_DS1302_RSTBIT == 27 /* * The only way to set g27 to output is to enable ATA. * * Make sure that R_GEN_CONFIG is setup correct. */ genconfig_shadow = ((genconfig_shadow & - ~IO_MASK(R_GEN_CONFIG, ata)) - | + ~IO_MASK(R_GEN_CONFIG, ata)) | (IO_STATE(R_GEN_CONFIG, ata, select))); *R_GEN_CONFIG = genconfig_shadow; - if (!ds1302_probe()) +#elif CONFIG_ETRAX_DS1302_RSTBIT == 0 + + /* Set the direction of this bit to out. */ + genconfig_shadow = ((genconfig_shadow & + ~IO_MASK(R_GEN_CONFIG, g0dir)) | + (IO_STATE(R_GEN_CONFIG, g0dir, out))); + *R_GEN_CONFIG = genconfig_shadow; +#endif + if (!ds1302_probe()) return -1; #else return -1; #endif } + /* Initialise trickle charger */ + ds1302_writereg(RTC_TRICKLECHARGER, + RTC_TCR_PATTERN |(CONFIG_ETRAX_DS1302_TRICKLE_CHARGE & 0x0F)); + return 0; +} + +static int __init ds1302_register(void) +{ + ds1302_init(); if (register_chrdev(RTC_MAJOR_NR, ds1302_name, &rtc_fops)) { printk(KERN_INFO "%s: unable to get major %d for rtc\n", ds1302_name, RTC_MAJOR_NR); return -1; } - return 0; + return 0; + } + +module_init(ds1302_register); diff --git a/arch/cris/drivers/eeprom.c b/arch/cris/arch-v10/drivers/eeprom.c index ae3c37886721..67523549dd96 100644 --- a/arch/cris/drivers/eeprom.c +++ b/arch/cris/arch-v10/drivers/eeprom.c @@ -20,8 +20,24 @@ *! in the spin-lock. *! *! $Log: eeprom.c,v $ -*! Revision 1.1.1.1 2001/12/17 13:59:27 bjornw -*! Import of Linux 2.5.1 +*! Revision 1.9 2003/07/04 08:27:37 starvik +*! Merge of Linux 2.5.74 +*! +*! Revision 1.8 2003/04/09 05:20:47 starvik +*! Merge of Linux 2.5.67 +*! +*! Revision 1.6 2003/02/10 07:19:28 starvik +*! Removed misplaced ; +*! +*! Revision 1.5 2002/12/11 13:13:57 starvik +*! Added arch/ to v10 specific includes +*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) +*! +*! Revision 1.4 2002/11/20 11:56:10 starvik +*! Merge of Linux 2.5.48 +*! +*! Revision 1.3 2002/11/18 13:16:06 starvik +*! Linux 2.5 port of latest 2.4 drivers *! *! Revision 1.8 2001/06/15 13:24:29 jonashg *! * Added verification of pointers from userspace in read and write. @@ -74,7 +90,7 @@ #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/smp_lock.h> +#include <linux/interrupt.h> #include <asm/uaccess.h> #include "i2c.h" @@ -425,9 +441,9 @@ int __init eeprom_init(void) static int eeprom_open(struct inode * inode, struct file * file) { - if(MINOR(inode->i_rdev) != EEPROM_MINOR_NR) + if(minor(inode->i_rdev) != EEPROM_MINOR_NR) return -ENXIO; - if(MAJOR(inode->i_rdev) != EEPROM_MAJOR_NR) + if(major(inode->i_rdev) != EEPROM_MAJOR_NR) return -ENXIO; if( eeprom.size > 0 ) @@ -449,39 +465,36 @@ static loff_t eeprom_lseek(struct file * file, loff_t offset, int orig) * orig 1: relative from current position * orig 2: position from last eeprom address */ - loff_t ret; - - lock_kernel(); + switch (orig) { case 0: - ret = file->f_pos = offset; + file->f_pos = offset; break; case 1: - ret = file->f_pos += offset; + file->f_pos += offset; break; case 2: - ret = file->f_pos = eeprom.size - offset; + file->f_pos = eeprom.size - offset; break; default: - ret = -EINVAL; + return -EINVAL; } /* truncate position */ if (file->f_pos < 0) { - file->f_pos = 0; - ret = -EOVERFLOW; + file->f_pos = 0; + return(-EOVERFLOW); } - + if (file->f_pos >= eeprom.size) { file->f_pos = eeprom.size - 1; - ret = -EOVERFLOW; + return(-EOVERFLOW); } - unlock_kernel(); - return ( ret ); + return ( file->f_pos ); } /* Reads data from eeprom. */ @@ -500,7 +513,7 @@ static int eeprom_read_buf(loff_t addr, char * buf, int count) 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; @@ -526,7 +539,7 @@ static ssize_t eeprom_read(struct file * file, char * buf, size_t count, loff_t 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%08X (%i) page: %i\n", eeprom_name, (int)p, (int)p, page); i2c_stop(); /* don't forget to wake them up */ @@ -609,7 +622,7 @@ static ssize_t eeprom_write(struct file * file, const char * buf, size_t count, if(!eeprom_address(p)) { printk(KERN_INFO "%s: Write failed to address the eeprom: " - "0x%08X (%i) \n", eeprom_name, p, p); + "0x%08X (%i) \n", eeprom_name, (int)p, (int)p); i2c_stop(); /* don't forget to wake them up */ @@ -747,7 +760,7 @@ static int eeprom_close(struct inode * inode, struct file * file) static int eeprom_address(unsigned long addr) { - int i, j; + int i; unsigned char page, offset; page = (unsigned char) (addr >> 8); diff --git a/arch/cris/drivers/ethernet.c b/arch/cris/arch-v10/drivers/ethernet.c index 9b72037ea030..6b9b40634da9 100644 --- a/arch/cris/drivers/ethernet.c +++ b/arch/cris/arch-v10/drivers/ethernet.c @@ -1,14 +1,109 @@ -/* $Id: ethernet.c,v 1.2 2001/12/18 13:35:15 bjornw Exp $ +/* $Id: ethernet.c,v 1.17 2003/07/04 08:27:37 starvik Exp $ * * e100net.c: A network driver for the ETRAX 100LX network controller. * - * Copyright (c) 1998-2001 Axis Communications AB. + * Copyright (c) 1998-2002 Axis Communications AB. * * The outline of this driver comes from skeleton.c. * * $Log: ethernet.c,v $ - * Revision 1.2 2001/12/18 13:35:15 bjornw - * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). + * Revision 1.17 2003/07/04 08:27:37 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.16 2003/04/24 08:28:22 starvik + * New LED behaviour: LED off when no link + * + * Revision 1.15 2003/04/09 05:20:47 starvik + * Merge of Linux 2.5.67 + * + * Revision 1.13 2003/03/06 16:11:01 henriken + * Off by one error in group address register setting. + * + * Revision 1.12 2003/02/27 17:24:19 starvik + * Corrected Rev to Revision + * + * Revision 1.11 2003/01/24 09:53:21 starvik + * Oops. Initialize GA to 0, not to 1 + * + * Revision 1.10 2003/01/24 09:50:55 starvik + * Initialize GA_0 and GA_1 to 0 to avoid matching of unwanted packets + * + * Revision 1.9 2002/12/13 07:40:58 starvik + * Added basic ethtool interface + * Handled out of memory when allocating new buffers + * + * Revision 1.8 2002/12/11 13:13:57 starvik + * Added arch/ to v10 specific includes + * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) + * + * Revision 1.7 2002/11/26 09:41:42 starvik + * Added e100_set_config (standard interface to set media type) + * Added protection against preemptive scheduling + * Added standard MII ioctls + * + * Revision 1.6 2002/11/21 07:18:18 starvik + * Timers must be initialized in 2.5.48 + * + * Revision 1.5 2002/11/20 11:56:11 starvik + * Merge of Linux 2.5.48 + * + * Revision 1.4 2002/11/18 07:26:46 starvik + * Linux 2.5 port of latest Linux 2.4 ethernet driver + * + * Revision 1.33 2002/10/02 20:16:17 hp + * SETF, SETS: Use underscored IO_x_ macros rather than incorrect token concatenation + * + * Revision 1.32 2002/09/16 06:05:58 starvik + * Align memory returned by dev_alloc_skb + * Moved handling of sent packets to interrupt to avoid reference counting problem + * + * Revision 1.31 2002/09/10 13:28:23 larsv + * Return -EINVAL for unknown ioctls to avoid confusing tools that tests + * for supported functionality by issuing special ioctls, i.e. wireless + * extensions. + * + * Revision 1.30 2002/05/07 18:50:08 johana + * Correct spelling in comments. + * + * Revision 1.29 2002/05/06 05:38:49 starvik + * Performance improvements: + * Large packets are not copied (breakpoint set to 256 bytes) + * The cache bug workaround is delayed until half of the receive list + * has been used + * Added transmit list + * Transmit interrupts are only enabled when transmit queue is full + * + * Revision 1.28.2.1 2002/04/30 08:15:51 starvik + * Performance improvements: + * Large packets are not copied (breakpoint set to 256 bytes) + * The cache bug workaround is delayed until half of the receive list + * has been used. + * Added transmit list + * Transmit interrupts are only enabled when transmit queue is full + * + * Revision 1.28 2002/04/22 11:47:21 johana + * Fix according to 2.4.19-pre7. time_after/time_before and + * missing end of comment. + * The patch has a typo for ethernet.c in e100_clear_network_leds(), + * that is fixed here. + * + * Revision 1.27 2002/04/12 11:55:11 bjornw + * Added TODO + * + * Revision 1.26 2002/03/15 17:11:02 bjornw + * Use prepare_rx_descriptor after the CPU has touched the receiving descs + * + * Revision 1.25 2002/03/08 13:07:53 bjornw + * Unnecessary spinlock removed + * + * Revision 1.24 2002/02/20 12:57:43 fredriks + * Replaced MIN() with min(). + * + * Revision 1.23 2002/02/20 10:58:14 fredriks + * Strip the Ethernet checksum (4 bytes) before forwarding a frame to upper layers. + * + * Revision 1.22 2002/01/30 07:48:22 matsfg + * Initiate R_NETWORK_TR_CTRL * * Revision 1.21 2001/11/23 11:54:49 starvik * Added IFF_PROMISC and IFF_ALLMULTI handling in set_multicast_list @@ -112,22 +207,25 @@ #include <linux/errno.h> #include <linux/init.h> +#include <linux/if.h> +#include <linux/mii.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> +#include <linux/ethtool.h> -#include <asm/svinto.h> /* DMA and register descriptions */ +#include <asm/arch/svinto.h>/* DMA and register descriptions */ #include <asm/io.h> /* LED_* I/O functions */ #include <asm/irq.h> #include <asm/dma.h> #include <asm/system.h> #include <asm/bitops.h> #include <asm/ethernet.h> +#include <asm/cache.h> //#define ETHDEBUG #define D(x) - /* * The name of the card. Is used for messages and in the requests for * io regions, irqs and dma channels @@ -154,6 +252,11 @@ struct net_local { spinlock_t lock; }; +typedef struct etrax_eth_descr +{ + etrax_dma_descr descr; + struct sk_buff* skb; +} etrax_eth_descr; /* Duplex settings */ enum duplex @@ -165,8 +268,6 @@ enum duplex /* Dma descriptors etc. */ -#define RX_BUF_SIZE 32768 - #define MAX_MEDIA_DATA_SIZE 1518 #define MIN_PACKET_LEN 46 @@ -207,29 +308,37 @@ enum duplex #define NO_NETWORK_ACTIVITY 0 #define NETWORK_ACTIVITY 1 -#define RX_DESC_BUF_SIZE 256 -#define NBR_OF_RX_DESC (RX_BUF_SIZE / \ - RX_DESC_BUF_SIZE) +#define NBR_OF_RX_DESC 64 +#define NBR_OF_TX_DESC 256 + +/* Large packets are sent directly to upper layers while small packets are */ +/* copied (to reduce memory waste). The following constant decides the breakpoint */ +#define RX_COPYBREAK 256 + +/* Due to a chip bug we need to flush the cache when descriptors are returned */ +/* to the DMA. To decrease performance impact we return descriptors in chunks. */ +/* The following constant determines the number of descriptors to return. */ +#define RX_QUEUE_THRESHOLD NBR_OF_RX_DESC/2 #define GET_BIT(bit,val) (((val) >> (bit)) & 0x01) /* Define some macros to access ETRAX 100 registers */ -#define SETF(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \ - IO_FIELD(##reg##, field, val) -#define SETS(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \ - IO_STATE(##reg##, field, val) +#define SETF(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \ + IO_FIELD_(reg##_, field##_, val) +#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \ + IO_STATE_(reg##_, field##_, _##val) -static etrax_dma_descr *myNextRxDesc; /* Points to the next descriptor to +static etrax_eth_descr *myNextRxDesc; /* Points to the next descriptor to to be processed */ -static etrax_dma_descr *myLastRxDesc; /* The last processed descriptor */ -static etrax_dma_descr *myPrevRxDesc; /* The descriptor right before myNextRxDesc */ - -static unsigned char RxBuf[RX_BUF_SIZE]; +static etrax_eth_descr *myLastRxDesc; /* The last processed descriptor */ +static etrax_eth_descr *myPrevRxDesc; /* The descriptor right before myNextRxDesc */ -static etrax_dma_descr RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned(4))); -static etrax_dma_descr TxDesc __attribute__ ((aligned(4))); +static etrax_eth_descr RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned(32))); -static struct sk_buff *tx_skb; +static etrax_eth_descr* myFirstTxDesc; /* First packet not yet sent */ +static etrax_eth_descr* myLastTxDesc; /* End of send queue */ +static etrax_eth_descr* myNextTxDesc; /* Next descriptor to use */ +static etrax_eth_descr TxDescList[NBR_OF_TX_DESC] __attribute__ ((aligned(32))); static unsigned int network_rec_config_shadow = 0; @@ -238,27 +347,29 @@ static struct timer_list speed_timer = TIMER_INITIALIZER(NULL, 0, 0); static struct timer_list clear_led_timer = TIMER_INITIALIZER(NULL, 0, 0); static int current_speed; /* Speed read from transceiver */ static int current_speed_selection; /* Speed selected by user */ -static int led_next_time; +static unsigned long led_next_time; static int led_active; +static int rx_queue_len; /* Duplex */ -static struct timer_list duplex_timer; +static struct timer_list duplex_timer = TIMER_INITIALIZER(NULL, 0, 0); static int full_duplex; static enum duplex current_duplex; /* Index to functions, as function prototypes. */ -static int etrax_ethernet_init(struct net_device *dev); +static int etrax_ethernet_init(void); static int e100_open(struct net_device *dev); static int e100_set_mac_address(struct net_device *dev, void *addr); static int e100_send_packet(struct sk_buff *skb, struct net_device *dev); -static void e100rx_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static void e100tx_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static void e100nw_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static irqreturn_t e100rxtx_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static irqreturn_t e100nw_interrupt(int irq, void *dev_id, struct pt_regs *regs); static void e100_rx(struct net_device *dev); static int e100_close(struct net_device *dev); static int e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static int e100_ethtool_ioctl(struct net_device* dev, struct ifreq *ifr); +static int e100_set_config(struct net_device* dev, struct ifmap* map); static void e100_tx_timeout(struct net_device *dev); static struct net_device_stats *e100_get_stats(struct net_device *dev); static void set_multicast_list(struct net_device *dev); @@ -273,6 +384,7 @@ static void e100_set_duplex(enum duplex); static void e100_negotiate(void); static unsigned short e100_get_mdio_reg(unsigned char reg_num); +static void e100_set_mdio_reg(unsigned char reg, unsigned short data); 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); @@ -296,15 +408,14 @@ etrax_ethernet_init(void) { struct net_device *dev; int i, err; - int anOffset = 0; - printk("ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2001 Axis Communications AB\n"); + printk("ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2003 Axis Communications AB\n"); dev = alloc_etherdev(sizeof(struct net_local)); if (!dev) return -ENOMEM; - dev->base_addr = (unsigned int)R_NETWORK_SA_0; /* just to have something to show */ + dev->base_addr = (unsigned int)R_NETWORK_SA_0; /* just to have something to show */ /* now setup our etrax specific stuff */ @@ -320,35 +431,55 @@ etrax_ethernet_init(void) dev->set_multicast_list = set_multicast_list; dev->set_mac_address = e100_set_mac_address; dev->do_ioctl = e100_ioctl; + dev->set_config = e100_set_config; dev->tx_timeout = e100_tx_timeout; /* Initialise the list of Etrax DMA-descriptors */ /* Initialise receive descriptors */ - for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) { - RxDescList[i].ctrl = 0; - RxDescList[i].sw_len = RX_DESC_BUF_SIZE; - RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]); - RxDescList[i].buf = virt_to_phys(RxBuf + anOffset); - RxDescList[i].status = 0; - RxDescList[i].hw_len = 0; - anOffset += RX_DESC_BUF_SIZE; + for (i = 0; i < NBR_OF_RX_DESC; i++) { + /* Allocate two extra cachelines to make sure that buffer used by DMA + * does not share cacheline with any other data (to avoid cache bug) + */ + RxDescList[i].skb = dev_alloc_skb(MAX_MEDIA_DATA_SIZE + 2 * L1_CACHE_BYTES); + RxDescList[i].descr.ctrl = 0; + RxDescList[i].descr.sw_len = MAX_MEDIA_DATA_SIZE; + RxDescList[i].descr.next = virt_to_phys(&RxDescList[i + 1]); + RxDescList[i].descr.buf = L1_CACHE_ALIGN(virt_to_phys(RxDescList[i].skb->data)); + RxDescList[i].descr.status = 0; + RxDescList[i].descr.hw_len = 0; + prepare_rx_descriptor(&RxDescList[i].descr); } - RxDescList[i].ctrl = d_eol; - RxDescList[i].sw_len = RX_DESC_BUF_SIZE; - RxDescList[i].next = virt_to_phys(&RxDescList[0]); - RxDescList[i].buf = virt_to_phys(RxBuf + anOffset); - RxDescList[i].status = 0; - RxDescList[i].hw_len = 0; + RxDescList[NBR_OF_RX_DESC - 1].descr.ctrl = d_eol; + RxDescList[NBR_OF_RX_DESC - 1].descr.next = virt_to_phys(&RxDescList[0]); + rx_queue_len = 0; + + /* Initialize transmit descriptors */ + for (i = 0; i < NBR_OF_TX_DESC; i++) { + TxDescList[i].descr.ctrl = 0; + TxDescList[i].descr.sw_len = 0; + TxDescList[i].descr.next = virt_to_phys(&TxDescList[i + 1].descr); + TxDescList[i].descr.buf = 0; + TxDescList[i].descr.status = 0; + TxDescList[i].descr.hw_len = 0; + TxDescList[i].skb = 0; + } + TxDescList[NBR_OF_TX_DESC - 1].descr.ctrl = d_eol; + TxDescList[NBR_OF_TX_DESC - 1].descr.next = virt_to_phys(&TxDescList[0].descr); + /* Initialise initial pointers */ - myNextRxDesc = &RxDescList[0]; - myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; - myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; + myNextRxDesc = &RxDescList[0]; + myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; + myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; + myFirstTxDesc = &TxDescList[0]; + myNextTxDesc = &TxDescList[0]; + myLastTxDesc = &TxDescList[NBR_OF_TX_DESC - 1]; + /* Register device */ err = register_netdev(dev); if (err) { kfree(dev); @@ -375,6 +506,10 @@ etrax_ethernet_init(void) 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; } @@ -385,9 +520,12 @@ etrax_ethernet_init(void) static int e100_set_mac_address(struct net_device *dev, void *p) { + struct net_local *np = (struct net_local *)dev->priv; struct sockaddr *addr = p; int i; + spin_lock(&np->lock); /* preemption protection */ + /* remember it */ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); @@ -412,6 +550,8 @@ e100_set_mac_address(struct net_device *dev, void *p) printk("%02X\n", dev->dev_addr[i]); + spin_unlock(&np->lock); + return 0; } @@ -462,14 +602,14 @@ e100_open(struct net_device *dev) /* allocate the irq corresponding to the receiving DMA */ - if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rx_interrupt, 0, + if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rxtx_interrupt, 0, cardname, (void *)dev)) { goto grace_exit0; } /* allocate the irq corresponding to the transmitting DMA */ - if (request_irq(NETWORK_DMA_TX_IRQ_NBR, e100tx_interrupt, 0, + if (request_irq(NETWORK_DMA_TX_IRQ_NBR, e100rxtx_interrupt, 0, cardname, (void *)dev)) { goto grace_exit1; } @@ -481,19 +621,6 @@ e100_open(struct net_device *dev) goto grace_exit2; } - /* - * Always allocate the DMA channels after the IRQ, - * and clean up on failure. - */ - - if (request_dma(NETWORK_TX_DMA_NBR, cardname)) { - goto grace_exit3; - } - - if (request_dma(NETWORK_RX_DMA_NBR, cardname)) { - goto grace_exit4; - } - /* give the HW an idea of what MAC address we want */ *R_NETWORK_SA_0 = dev->dev_addr[0] | (dev->dev_addr[1] << 8) | @@ -518,22 +645,28 @@ e100_open(struct net_device *dev) IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) | IO_STATE(R_NETWORK_GEN_CONFIG, enable, on); + *R_NETWORK_TR_CTRL = + IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr) | + IO_STATE(R_NETWORK_TR_CTRL, delay, none) | + IO_STATE(R_NETWORK_TR_CTRL, cancel, dont) | + IO_STATE(R_NETWORK_TR_CTRL, cd, enable) | + IO_STATE(R_NETWORK_TR_CTRL, retry, enable) | + IO_STATE(R_NETWORK_TR_CTRL, pad, enable) | + IO_STATE(R_NETWORK_TR_CTRL, crc, enable); + save_flags(flags); cli(); /* enable the irq's for ethernet DMA */ *R_IRQ_MASK2_SET = - IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) | - IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set); + IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set); *R_IRQ_MASK0_SET = IO_STATE(R_IRQ_MASK0_SET, overrun, set) | IO_STATE(R_IRQ_MASK0_SET, underrun, set) | IO_STATE(R_IRQ_MASK0_SET, excessive_col, set); - tx_skb = 0; - /* make sure the irqs are cleared */ *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do); @@ -549,6 +682,11 @@ e100_open(struct net_device *dev) *R_DMA_CH1_FIRST = virt_to_phys(myNextRxDesc); *R_DMA_CH1_CMD = IO_STATE(R_DMA_CH1_CMD, cmd, start); + /* Set up transmit DMA channel so it can be restarted later */ + + *R_DMA_CH0_FIRST = 0; + *R_DMA_CH0_DESCR = virt_to_phys(myLastTxDesc); + restore_flags(flags); /* We are now ready to accept transmit requeusts from @@ -558,10 +696,6 @@ e100_open(struct net_device *dev) return 0; -grace_exit4: - free_dma(NETWORK_TX_DMA_NBR); -grace_exit3: - free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev); grace_exit2: free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev); grace_exit1: @@ -596,9 +730,7 @@ e100_check_speed(unsigned long dummy) static void e100_negotiate(void) { - unsigned short cmd; unsigned short data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG); - int bitCounter; /* Discard old speed and duplex settings */ data &= ~(MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | @@ -637,29 +769,13 @@ e100_negotiate(void) MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD; } - cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) | - (MDIO_ADVERTISMENT_REG<< 2); - - e100_send_mdio_cmd(cmd, 1); - - /* Data... */ - for (bitCounter=15; bitCounter>=0 ; bitCounter--) { - e100_send_mdio_bit(GET_BIT(bitCounter, data)); - } + e100_set_mdio_reg(MDIO_ADVERTISMENT_REG, data); /* Renegotiate with link partner */ data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG); data |= MDIO_BC_NEGOTIATE; - cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) | - (MDIO_BASE_CONTROL_REG<< 2); - - e100_send_mdio_cmd(cmd, 1); - - /* Data... */ - for (bitCounter=15; bitCounter>=0 ; bitCounter--) { - e100_send_mdio_bit(GET_BIT(bitCounter, data)); - } + e100_set_mdio_reg(MDIO_BASE_CONTROL_REG, data); } static void @@ -727,6 +843,24 @@ e100_get_mdio_reg(unsigned char reg_num) } static void +e100_set_mdio_reg(unsigned char reg, unsigned short data) +{ + int bitCounter; + unsigned short cmd; + + cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) | + (reg << 2); + + e100_send_mdio_cmd(cmd, 1); + + /* Data... */ + for (bitCounter=15; bitCounter>=0 ; bitCounter--) { + e100_send_mdio_bit(GET_BIT(bitCounter, data)); + } + +} + +static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd) { int bitCounter; @@ -801,6 +935,9 @@ static void e100_tx_timeout(struct net_device *dev) { struct net_local *np = (struct net_local *)dev->priv; + unsigned long flags; + + spin_lock_irqsave(&np->lock, flags); printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, tx_done(dev) ? "IRQ problem" : "network cable problem"); @@ -818,14 +955,22 @@ e100_tx_timeout(struct net_device *dev) e100_reset_transceiver(); - /* and get rid of the packet that never got an interrupt */ - - dev_kfree_skb(tx_skb); - tx_skb = 0; - + /* and get rid of the packets that never got an interrupt */ + while (myFirstTxDesc != myNextTxDesc) + { + dev_kfree_skb(myFirstTxDesc->skb); + myFirstTxDesc->skb = 0; + myFirstTxDesc = phys_to_virt(myFirstTxDesc->descr.next); + } + + /* Set up transmit DMA channel so it can be restarted later */ + *R_DMA_CH0_FIRST = 0; + *R_DMA_CH0_DESCR = virt_to_phys(myLastTxDesc); + /* tell the upper layers we're ok again */ netif_wake_queue(dev); + spin_unlock_irqrestore(&np->lock, flags); } @@ -841,25 +986,30 @@ e100_send_packet(struct sk_buff *skb, struct net_device *dev) struct net_local *np = (struct net_local *)dev->priv; int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; + unsigned long flags; #ifdef ETHDEBUG printk("send packet len %d\n", length); #endif - spin_lock_irq(&np->lock); /* protect from tx_interrupt */ + spin_lock_irqsave(&np->lock, flags); /* protect from tx_interrupt and ourself */ + + myNextTxDesc->skb = skb; - tx_skb = skb; /* remember it so we can free it in the tx irq handler later */ dev->trans_start = jiffies; e100_hardware_send_packet(buf, length); - /* this simple TX driver has only one send-descriptor so we're full - * directly. If this had a send-ring instead, we would only do this if - * the ring got full. - */ + myNextTxDesc = phys_to_virt(myNextTxDesc->descr.next); - netif_stop_queue(dev); + /* 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); + } - spin_unlock_irq(&np->lock); + spin_unlock_irqrestore(&np->lock, flags); return 0; } @@ -869,12 +1019,14 @@ e100_send_packet(struct sk_buff *skb, struct net_device *dev) * Handle the network interface interrupts. */ -static void -e100rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t +e100rxtx_interrupt(int irq, void *dev_id, struct pt_regs * regs) { struct net_device *dev = (struct net_device *)dev_id; + struct net_local *np = (struct net_local *)dev->priv; unsigned long irqbits = *R_IRQ_MASK2_RD; + /* Handle received packets */ if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) { /* acknowledge the eop interrupt */ @@ -899,51 +1051,31 @@ e100rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) so we have to loop back and check if so */ } } -} - -/* the transmit dma channel interrupt - * - * this is supposed to free the skbuff which was pending during transmission, - * and inform the kernel that we can send one more buffer - */ -static void -e100tx_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - unsigned long irqbits = *R_IRQ_MASK2_RD; - struct net_local *np = (struct net_local *)dev->priv; - - /* check for a dma0_eop interrupt */ - if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) { - /* This protects us from concurrent execution of - * our dev->hard_start_xmit function above. - */ - - spin_lock(&np->lock); - - /* acknowledge the eop interrupt */ + /* Report any packets that have been sent */ + while (myFirstTxDesc != phys_to_virt(*R_DMA_CH0_FIRST) && + myFirstTxDesc != myNextTxDesc) + { + np->stats.tx_bytes += myFirstTxDesc->skb->len; + np->stats.tx_packets++; + + /* dma is ready with the transmission of the data in tx_skb, so now + we can release the skb memory */ + dev_kfree_skb_irq(myFirstTxDesc->skb); + myFirstTxDesc->skb = 0; + myFirstTxDesc = phys_to_virt(myFirstTxDesc->descr.next); + } + 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); - - if (*R_DMA_CH0_FIRST == 0 && tx_skb) { - np->stats.tx_bytes += tx_skb->len; - np->stats.tx_packets++; - /* dma is ready with the transmission of the data in tx_skb, so now - we can release the skb memory */ - dev_kfree_skb_irq(tx_skb); - tx_skb = 0; - netif_wake_queue(dev); - } else { - printk(KERN_WARNING "%s: tx weird interrupt\n", - cardname); - } - - spin_unlock(&np->lock); + *R_IRQ_MASK2_CLR = IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr); + netif_wake_queue(dev); } + return IRQ_HANDLED; } -static void +static irqreturn_t e100nw_interrupt(int irq, void *dev_id, struct pt_regs * regs) { struct net_device *dev = (struct net_device *)dev_id; @@ -968,7 +1100,7 @@ e100nw_interrupt(int irq, void *dev_id, struct pt_regs * regs) np->stats.tx_errors++; D(printk("ethernet excessive collisions!\n")); } - + return IRQ_HANDLED; } /* We have a good packet(s), get it/them out of the buffers. */ @@ -978,7 +1110,6 @@ e100_rx(struct net_device *dev) struct sk_buff *skb; int length = 0; struct net_local *np = (struct net_local *)dev->priv; - struct etrax_dma_descr *mySaveRxDesc = myNextRxDesc; unsigned char *skb_data_ptr; #ifdef ETHDEBUG int i; @@ -994,24 +1125,13 @@ e100_rx(struct net_device *dev) mod_timer(&clear_led_timer, jiffies + HZ/10); } - /* If the packet is broken down in many small packages then merge - * count how much space we will need to alloc with skb_alloc() for - * it to fit. - */ - - while (!(myNextRxDesc->status & d_eop)) { - length += myNextRxDesc->sw_len; /* use sw_len for the first descs */ - myNextRxDesc->status = 0; - myNextRxDesc = phys_to_virt(myNextRxDesc->next); - } - - length += myNextRxDesc->hw_len; /* use hw_len for the last descr */ + length = myNextRxDesc->descr.hw_len - 4; ((struct net_local *)dev->priv)->stats.rx_bytes += length; #ifdef ETHDEBUG printk("Got a packet of length %d:\n", length); /* dump the first bytes in the packet */ - skb_data_ptr = (unsigned char *)phys_to_virt(mySaveRxDesc->buf); + skb_data_ptr = (unsigned char *)phys_to_virt(myNextRxDesc->descr.buf); for (i = 0; i < 8; i++) { printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8, skb_data_ptr[0],skb_data_ptr[1],skb_data_ptr[2],skb_data_ptr[3], @@ -1020,53 +1140,68 @@ e100_rx(struct net_device *dev) } #endif - skb = dev_alloc_skb(length - ETHER_HEAD_LEN); - if (!skb) { - np->stats.rx_errors++; - printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", - dev->name); - return; - } + if (length < RX_COPYBREAK) { + /* Small packet, copy data */ + skb = dev_alloc_skb(length - ETHER_HEAD_LEN); + if (!skb) { + np->stats.rx_errors++; + printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); + return; + } - skb_put(skb, length - ETHER_HEAD_LEN); /* allocate room for the packet body */ - skb_data_ptr = skb_push(skb, ETHER_HEAD_LEN); /* allocate room for the header */ + skb_put(skb, length - ETHER_HEAD_LEN); /* allocate room for the packet body */ + skb_data_ptr = skb_push(skb, ETHER_HEAD_LEN); /* allocate room for the header */ #ifdef ETHDEBUG - printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n", - skb->head, skb->data, skb->tail, skb->end); - printk("copying packet to 0x%x.\n", skb_data_ptr); + printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n", + skb->head, skb->data, skb->tail, skb->end); + printk("copying packet to 0x%x.\n", skb_data_ptr); #endif - - /* this loop can be made using max two memcpy's if optimized */ - - while (mySaveRxDesc != myNextRxDesc) { - memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf), - mySaveRxDesc->sw_len); - skb_data_ptr += mySaveRxDesc->sw_len; - mySaveRxDesc = phys_to_virt(mySaveRxDesc->next); + + memcpy(skb_data_ptr, phys_to_virt(myNextRxDesc->descr.buf), length); + } + else { + /* Large packet, send directly to upper layers and allocate new + * memory (aligned to cache line boundary to avoid bug). + * Before sending the skb to upper layers we must make sure that + * skb->data points to the aligned start of the packet. + */ + int align; + struct sk_buff *new_skb = dev_alloc_skb(MAX_MEDIA_DATA_SIZE + 2 * L1_CACHE_BYTES); + if (!new_skb) { + np->stats.rx_errors++; + printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); + return; + } + skb = myNextRxDesc->skb; + align = (int)phys_to_virt(myNextRxDesc->descr.buf) - (int)skb->data; + skb_put(skb, length + align); + skb_pull(skb, align); /* Remove alignment bytes */ + myNextRxDesc->skb = new_skb; + myNextRxDesc->descr.buf = L1_CACHE_ALIGN(virt_to_phys(myNextRxDesc->skb->data)); } - - memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf), - mySaveRxDesc->hw_len); skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); /* Send the packet to the upper layers */ - netif_rx(skb); /* Prepare for next packet */ - - myNextRxDesc->status = 0; + myNextRxDesc->descr.status = 0; myPrevRxDesc = myNextRxDesc; - myNextRxDesc = phys_to_virt(myNextRxDesc->next); + myNextRxDesc = phys_to_virt(myNextRxDesc->descr.next); - myPrevRxDesc->ctrl |= d_eol; - myLastRxDesc->ctrl &= ~d_eol; - myLastRxDesc = myPrevRxDesc; + rx_queue_len++; - return; + /* Check if descriptors should be returned */ + if (rx_queue_len == RX_QUEUE_THRESHOLD) { + flush_etrax_cache(); + myPrevRxDesc->descr.ctrl |= d_eol; + myLastRxDesc->descr.ctrl &= ~d_eol; + myLastRxDesc = myPrevRxDesc; + rx_queue_len = 0; + } } /* The inverse routine to net_open(). */ @@ -1105,9 +1240,6 @@ e100_close(struct net_device *dev) free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev); free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev); - free_dma(NETWORK_TX_DMA_NBR); - free_dma(NETWORK_RX_DMA_NBR); - /* Update the statistics here. */ update_rx_stats(&np->stats); @@ -1119,8 +1251,24 @@ e100_close(struct net_device *dev) static int e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - /* Maybe default should return -EINVAL instead? */ + struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data; + struct net_local *np = (struct net_local *)dev->priv; + + spin_lock(&np->lock); /* Preempt protection */ switch (cmd) { + case SIOCETHTOOL: + return e100_ethtool_ioctl(dev,ifr); + case SIOCGMIIPHY: /* Get PHY address */ + data->phy_id = MDIO_PHYS_ADDR; + break; + case SIOCGMIIREG: /* Read MII register */ + data->val_out = e100_get_mdio_reg(data->reg_num); + break; + case SIOCSMIIREG: /* Write MII register */ + e100_set_mdio_reg(data->reg_num, data->val_in); + break; + /* The ioctls below should be considered obsolete but are */ + /* still present for compatability with old scripts/apps */ case SET_ETH_SPEED_10: /* 10 Mbps */ e100_set_speed(10); break; @@ -1139,11 +1287,128 @@ e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) case SET_ETH_DUPLEX_AUTO: /* Autonegotiate duplex*/ e100_set_duplex(autoneg); break; - default: /* Auto neg */ + default: + return -EINVAL; + } + spin_unlock(&np->lock); + return 0; +} + +static int +e100_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr) +{ + struct ethtool_cmd ecmd; + + if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd))) + return -EFAULT; + + switch (ecmd.cmd) { + case ETHTOOL_GSET: + { + memset((void *) &ecmd, 0, sizeof (ecmd)); + ecmd.supported = + SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII | + SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; + ecmd.port = PORT_TP; + ecmd.transceiver = XCVR_EXTERNAL; + ecmd.phy_address = MDIO_PHYS_ADDR; + ecmd.speed = current_speed; + ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + ecmd.advertising = ADVERTISED_TP; + if (current_duplex == autoneg && current_speed_selection == 0) + ecmd.advertising = ADVERTISED_Autoneg; + else { + ecmd.advertising |= + ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full; + if (current_speed_selection == 10) + ecmd.advertising &= ~(ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full); + else if (current_speed_selection == 100) + ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full); + if (current_duplex == half) + ecmd.advertising &= ~(ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Full); + else if (current_duplex == full) + ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_100baseT_Half); + } + ecmd.autoneg = AUTONEG_ENABLE; + if (copy_to_user(ifr->ifr_data, &ecmd, sizeof (ecmd))) + return -EFAULT; + } + break; + case ETHTOOL_SSET: + { + if (!capable(CAP_NET_ADMIN)) { + return -EPERM; + } + if (ecmd.autoneg == AUTONEG_ENABLE) { + e100_set_duplex(autoneg); + e100_set_speed(0); + } else { + e100_set_duplex(ecmd.duplex == DUPLEX_HALF ? half : full); + e100_set_speed(ecmd.speed == SPEED_10 ? 10: 100); + } + } + break; + case ETHTOOL_GDRVINFO: + { + struct ethtool_drvinfo info; + memset((void *) &info, 0, sizeof (info)); + strncpy(info.driver, "ETRAX 100LX", sizeof(info.driver) - 1); + strncpy(info.version, "$Revision: 1.17 $", 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; + info.eedump_len = 0; + info.testinfo_len = 0; + if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) + return -EFAULT; + } + break; + case ETHTOOL_NWAY_RST: + if (current_duplex == autoneg && current_speed_selection == 0) + e100_negotiate(); + break; + default: + return -EOPNOTSUPP; + break; + } + return 0; +} + +static int +e100_set_config(struct net_device *dev, struct ifmap *map) +{ + struct net_local *np = (struct net_local *)dev->priv; + spin_lock(&np->lock); /* Preempt protection */ + + switch(map->port) { + case IF_PORT_UNKNOWN: + /* Use autoneg */ e100_set_speed(0); e100_set_duplex(autoneg); break; + case IF_PORT_10BASET: + e100_set_speed(10); + e100_set_duplex(autoneg); + break; + case IF_PORT_100BASET: + case IF_PORT_100BASETX: + e100_set_speed(100); + e100_set_duplex(autoneg); + break; + case IF_PORT_100BASEFX: + case IF_PORT_10BASE2: + case IF_PORT_AUI: + spin_unlock(&np->lock); + return -EOPNOTSUPP; + break; + default: + printk(KERN_ERR "%s: Invalid media selected", dev->name); + spin_unlock(&np->lock); + return -EINVAL; } + spin_unlock(&np->lock); return 0; } @@ -1177,10 +1442,13 @@ static struct net_device_stats * e100_get_stats(struct net_device *dev) { struct net_local *lp = (struct net_local *)dev->priv; + unsigned long flags; + spin_lock_irqsave(&lp->lock, flags); update_rx_stats(&lp->stats); update_tx_stats(&lp->stats); - + + spin_unlock_irqrestore(&lp->lock, flags); return &lp->stats; } @@ -1194,9 +1462,11 @@ e100_get_stats(struct net_device *dev) static void set_multicast_list(struct net_device *dev) { + struct net_local *lp = (struct net_local *)dev->priv; int num_addr = dev->mc_count; unsigned long int lo_bits; unsigned long int hi_bits; + spin_lock(&lp->lock); if (dev->flags & IFF_PROMISC) { /* promiscuous mode */ @@ -1255,7 +1525,7 @@ set_multicast_list(struct net_device *dev) hash_ix &= 0x3f; - if (hash_ix > 32) { + if (hash_ix >= 32) { hi_bits |= (1 << (hash_ix-32)); } else { @@ -1269,6 +1539,7 @@ set_multicast_list(struct net_device *dev) } *R_NETWORK_GA_0 = lo_bits; *R_NETWORK_GA_1 = hi_bits; + spin_unlock(&lp->lock); } void @@ -1287,15 +1558,16 @@ e100_hardware_send_packet(char *buf, int length) } /* configure the tx dma descriptor */ + myNextTxDesc->descr.sw_len = length; + myNextTxDesc->descr.ctrl = d_eop | d_eol | d_wait; + myNextTxDesc->descr.buf = virt_to_phys(buf); - TxDesc.sw_len = length; - TxDesc.ctrl = d_eop | d_eol | d_wait; - TxDesc.buf = virt_to_phys(buf); + /* Move end of list */ + myLastTxDesc->descr.ctrl &= ~d_eol; + myLastTxDesc = myNextTxDesc; - /* setup the dma channel and start it */ - - *R_DMA_CH0_FIRST = virt_to_phys(&TxDesc); - *R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, start); + /* Restart DMA channel */ + *R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, restart); } static void @@ -1323,7 +1595,7 @@ e100_set_network_leds(int active) 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) { diff --git a/arch/cris/arch-v10/drivers/gpio.c b/arch/cris/arch-v10/drivers/gpio.c new file mode 100644 index 000000000000..a105cadaeed1 --- /dev/null +++ b/arch/cris/arch-v10/drivers/gpio.c @@ -0,0 +1,907 @@ +/* $Id: gpio.c,v 1.8 2003/07/04 08:27:37 starvik Exp $ + * + * Etrax general port I/O device + * + * Copyright (c) 1999, 2000, 2001, 2002 Axis Communications AB + * + * Authors: Bjorn Wesen (initial version) + * Ola Knutsson (LED handling) + * Johan Adolfsson (read/set directions, write, port G) + * + * $Log: gpio.c,v $ + * Revision 1.8 2003/07/04 08:27:37 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.7 2003/01/10 07:44:07 starvik + * init_ioremap is now called by kernel before drivers are initialized + * + * Revision 1.6 2002/12/11 13:13:57 starvik + * Added arch/ to v10 specific includes + * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) + * + * Revision 1.5 2002/11/20 11:56:11 starvik + * Merge of Linux 2.5.48 + * + * Revision 1.4 2002/11/18 10:10:05 starvik + * Linux 2.5 port of latest gpio.c from Linux 2.4 + * + * Revision 1.20 2002/10/16 21:16:24 johana + * Added support for PA high level interrupt. + * That gives 2ms response time with iodtest for high levels and 2-12 ms + * response time on low levels if the check is not made in + * process.c:cpu_idle() as well. + * + * Revision 1.19 2002/10/14 18:27:33 johana + * Implemented alarm handling so select() now works. + * Latency is around 6-9 ms with a etrax_gpio_wake_up_check() in + * cpu_idle(). + * Otherwise I get 15-18 ms (same as doing the poll in userspace - + * but less overhead). + * TODO? Perhaps we should add the check in IMMEDIATE_BH (or whatever it + * is in 2.4) as well? + * TODO? Perhaps call request_irq()/free_irq() only when needed? + * Increased version to 2.5 + * + * Revision 1.18 2002/10/11 15:02:00 johana + * Mask inverted 8 bit value in setget_input(). + * + * Revision 1.17 2002/06/17 15:53:01 johana + * Added IO_READ_INBITS, IO_READ_OUTBITS, IO_SETGET_INPUT and IO_SETGET_OUTPUT + * that take a pointer as argument and thus can handle 32 bit ports (G) + * correctly. + * These should be used instead of IO_READBITS, IO_SETINPUT and IO_SETOUTPUT. + * (especially if Port G bit 31 is used) + * + * Revision 1.16 2002/06/17 09:59:51 johana + * Returning 32 bit values in the ioctl return value doesn't work if bit + * 31 is set (could happen for port G), so mask it of with 0x7FFFFFFF. + * A new set of ioctl's will be added. + * + * Revision 1.15 2002/05/06 13:19:13 johana + * IO_SETINPUT returns mask with bit set = inputs for PA and PB as well. + * + * Revision 1.14 2002/04/12 12:01:53 johana + * Use global r_port_g_data_shadow. + * Moved gpio_init_port_g() closer to gpio_init() and marked it __init. + * + * Revision 1.13 2002/04/10 12:03:55 johana + * Added support for port G /dev/gpiog (minor 3). + * Changed indentation on switch cases. + * Fixed other spaces to tabs. + * + * Revision 1.12 2001/11/12 19:42:15 pkj + * * Corrected return values from gpio_leds_ioctl(). + * * Fixed compiler warnings. + * + * Revision 1.11 2001/10/30 14:39:12 johana + * Added D() around gpio_write printk. + * + * Revision 1.10 2001/10/25 10:24:42 johana + * Added IO_CFG_WRITE_MODE ioctl and write method that can do fast + * bittoggling in the kernel. (This speeds up programming an FPGA with 450kB + * from ~60 seconds to 4 seconds). + * Added save_flags/cli/restore_flags in ioctl. + * + * Revision 1.9 2001/05/04 14:16:07 matsfg + * Corrected spelling error + * + * Revision 1.8 2001/04/27 13:55:26 matsfg + * Moved initioremap. + * Turns off all LEDS on init. + * Added support for shutdown and powerbutton. + * + * Revision 1.7 2001/04/04 13:30:08 matsfg + * Added bitset and bitclear for leds. Calls init_ioremap to set up memmapping + * + * Revision 1.6 2001/03/26 16:03:06 bjornw + * Needs linux/config.h + * + * Revision 1.5 2001/03/26 14:22:03 bjornw + * Namechange of some config options + * + * Revision 1.4 2001/02/27 13:52:48 bjornw + * malloc.h -> slab.h + * + * Revision 1.3 2001/01/24 15:06:48 bjornw + * gpio_wq correct type + * + * Revision 1.2 2001/01/18 16:07:30 bjornw + * 2.4 port + * + * Revision 1.1 2001/01/18 15:55:16 bjornw + * Verbatim copy of etraxgpio.c from elinux 2.0 added + * + * + */ + +#include <linux/config.h> + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/interrupt.h> + +#include <asm/etraxgpio.h> +#include <asm/arch/svinto.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/irq.h> + +#define GPIO_MAJOR 120 /* experimental MAJOR number */ + +#define D(x) + +#if 0 +static int dp_cnt; +#define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0) +#else +#define DP(x) +#endif + +static char gpio_name[] = "etrax gpio"; + +#if 0 +static wait_queue_head_t *gpio_wq; +#endif + +static int gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static ssize_t gpio_write(struct file * file, const char * buf, size_t count, + loff_t *off); +static int gpio_open(struct inode *inode, struct file *filp); +static int gpio_release(struct inode *inode, struct file *filp); +static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait); + +/* private data per open() of this driver */ + +struct gpio_private { + struct gpio_private *next; + /* These fields are for PA and PB only */ + volatile unsigned char *port, *shadow; + volatile unsigned char *dir, *dir_shadow; + unsigned char changeable_dir; + unsigned char changeable_bits; + unsigned char clk_mask; + unsigned char data_mask; + unsigned char write_msb; + unsigned char pad1, pad2, pad3; + /* These fields are generic */ + unsigned long highalarm, lowalarm; + wait_queue_head_t alarm_wq; + int minor; +}; + +/* linked list of alarms to check for */ + +static struct gpio_private *alarmlist = 0; + +static int gpio_some_alarms = 0; /* Set if someone uses alarm */ + +/* Port A and B use 8 bit access, but Port G is 32 bit */ +#define NUM_PORTS (GPIO_MINOR_B+1) + +static volatile unsigned char *ports[NUM_PORTS] = { + R_PORT_PA_DATA, + R_PORT_PB_DATA, +}; +static volatile unsigned char *shads[NUM_PORTS] = { + &port_pa_data_shadow, + &port_pb_data_shadow +}; + +/* What direction bits that are user changeable 1=changeable*/ +#ifndef CONFIG_ETRAX_PA_CHANGEABLE_DIR +#define CONFIG_ETRAX_PA_CHANGEABLE_DIR 0x00 +#endif +#ifndef CONFIG_ETRAX_PB_CHANGEABLE_DIR +#define CONFIG_ETRAX_PB_CHANGEABLE_DIR 0x00 +#endif + +#ifndef CONFIG_ETRAX_PA_CHANGEABLE_BITS +#define CONFIG_ETRAX_PA_CHANGEABLE_BITS 0xFF +#endif +#ifndef CONFIG_ETRAX_PB_CHANGEABLE_BITS +#define CONFIG_ETRAX_PB_CHANGEABLE_BITS 0xFF +#endif + + +static unsigned char changeable_dir[NUM_PORTS] = { + CONFIG_ETRAX_PA_CHANGEABLE_DIR, + CONFIG_ETRAX_PB_CHANGEABLE_DIR +}; +static unsigned char changeable_bits[NUM_PORTS] = { + CONFIG_ETRAX_PA_CHANGEABLE_BITS, + CONFIG_ETRAX_PB_CHANGEABLE_BITS +}; + +static volatile unsigned char *dir[NUM_PORTS] = { + R_PORT_PA_DIR, + R_PORT_PB_DIR +}; + +static volatile unsigned char *dir_shadow[NUM_PORTS] = { + &port_pa_dir_shadow, + &port_pb_dir_shadow +}; + +/* Port G is 32 bit, handle it special, some bits are both inputs + and outputs at the same time, only some of the bits can change direction + and some of them in groups of 8 bit. */ +static unsigned long changeable_dir_g; +static unsigned long dir_g_in_bits; +static unsigned long dir_g_out_bits; +static unsigned long dir_g_shadow; /* 1=output */ + +#define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B) + + + +static unsigned int +gpio_poll(struct file *file, + poll_table *wait) +{ + unsigned int mask = 0; + struct gpio_private *priv = (struct gpio_private *)file->private_data; + unsigned long data; + poll_wait(file, &priv->alarm_wq, wait); + if (priv->minor == GPIO_MINOR_A) { + unsigned long 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); + } else if (priv->minor == GPIO_MINOR_B) + data = *R_PORT_PB_DATA; + else if (priv->minor == GPIO_MINOR_G) + data = *R_PORT_G_DATA; + else + return 0; + + if ((data & priv->highalarm) || + (~data & priv->lowalarm)) { + mask = POLLIN|POLLRDNORM; + } + + DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); + return mask; +} + +int etrax_gpio_wake_up_check(void) +{ + struct gpio_private *priv = alarmlist; + unsigned long data = 0; + int ret = 0; + while (priv) { + if (USE_PORTS(priv)) { + data = *priv->port; + } else if (priv->minor == GPIO_MINOR_G) { + data = *R_PORT_G_DATA; + } + if ((data & priv->highalarm) || + (~data & priv->lowalarm)) { + DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); + wake_up_interruptible(&priv->alarm_wq); + ret = 1; + } + priv = priv->next; + } + return ret; +} + +static irqreturn_t +gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + if (gpio_some_alarms) { + etrax_gpio_wake_up_check(); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static irqreturn_t +gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long tmp; + /* Find what PA interrupts are active */ + tmp = (*R_IRQ_READ1 >> R_IRQ_READ1__pa0__BITNR) & 0xFF; + /* 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); + if (gpio_some_alarms) { + return IRQ_RETVAL(etrax_gpio_wake_up_check()); + } + return IRQ_NONE; +} + + +static ssize_t gpio_write(struct file * file, const char * buf, size_t count, + loff_t *off) +{ + struct gpio_private *priv = (struct gpio_private *)file->private_data; + unsigned char data, clk_mask, data_mask, write_msb; + unsigned long flags; + ssize_t retval = count; + if (priv->minor !=GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) { + return -EFAULT; + } + + if (verify_area(VERIFY_READ, buf, count)) { + return -EFAULT; + } + clk_mask = priv->clk_mask; + data_mask = priv->data_mask; + /* It must have been configured using the IO_CFG_WRITE_MODE */ + /* Perhaps a better error code? */ + if (clk_mask == 0 || data_mask == 0) { + return -EPERM; + } + write_msb = priv->write_msb; + D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb)); + while (count--) { + int i; + data = *buf++; + if (priv->write_msb) { + for (i = 7; i >= 0;i--) { + local_irq_save(flags); local_irq_disable(); + *priv->port = *priv->shadow &= ~clk_mask; + if (data & 1<<i) + *priv->port = *priv->shadow |= data_mask; + else + *priv->port = *priv->shadow &= ~data_mask; + /* For FPGA: min 5.0ns (DCC) before CCLK high */ + *priv->port = *priv->shadow |= clk_mask; + local_irq_restore(flags); + } + } else { + for (i = 0; i <= 7;i++) { + local_irq_save(flags); local_irq_disable(); + *priv->port = *priv->shadow &= ~clk_mask; + if (data & 1<<i) + *priv->port = *priv->shadow |= data_mask; + else + *priv->port = *priv->shadow &= ~data_mask; + /* For FPGA: min 5.0ns (DCC) before CCLK high */ + *priv->port = *priv->shadow |= clk_mask; + local_irq_restore(flags); + } + } + } + return retval; +} + + + +static int +gpio_open(struct inode *inode, struct file *filp) +{ + struct gpio_private *priv; + int p = minor(inode->i_rdev); + + if (p > GPIO_MINOR_LAST) + return -EINVAL; + + priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private), + GFP_KERNEL); + + if (!priv) + return -ENOMEM; + + priv->minor = p; + + /* initialize the io/alarm struct and link it into our alarmlist */ + + priv->next = alarmlist; + alarmlist = priv; + if (USE_PORTS(priv)) { /* A and B */ + priv->port = ports[p]; + priv->shadow = shads[p]; + priv->dir = dir[p]; + priv->dir_shadow = dir_shadow[p]; + priv->changeable_dir = changeable_dir[p]; + priv->changeable_bits = changeable_bits[p]; + } else { + priv->port = NULL; + priv->shadow = NULL; + priv->dir = NULL; + priv->dir_shadow = NULL; + priv->changeable_dir = 0; + priv->changeable_bits = 0; + } + + priv->highalarm = 0; + priv->lowalarm = 0; + priv->clk_mask = 0; + priv->data_mask = 0; + init_waitqueue_head(&priv->alarm_wq); + + filp->private_data = (void *)priv; + + return 0; +} + +static int +gpio_release(struct inode *inode, struct file *filp) +{ + struct gpio_private *p = alarmlist; + struct gpio_private *todel = (struct gpio_private *)filp->private_data; + + /* unlink from alarmlist and free the private structure */ + + if (p == todel) { + alarmlist = todel->next; + } else { + while (p->next != todel) + p = p->next; + p->next = todel->next; + } + + kfree(todel); + /* Check if there are still any alarms set */ + p = alarmlist; + while (p) { + if (p->highalarm | p->lowalarm) { + gpio_some_alarms = 1; + return 0; + } + p = p->next; + } + gpio_some_alarms = 0; + + return 0; +} + +/* Main device API. ioctl's to read/set/clear bits, as well as to + * set alarms to wait for using a subsequent select(). + */ + +unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) +{ + /* Set direction 0=unchanged 1=input, + * return mask with 1=input + */ + unsigned long flags; + if (USE_PORTS(priv)) { + local_irq_save(flags); local_irq_disable(); + *priv->dir = *priv->dir_shadow &= + ~((unsigned char)arg & priv->changeable_dir); + local_irq_restore(flags); + return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */ + } else if (priv->minor == GPIO_MINOR_G) { + /* We must fiddle with R_GEN_CONFIG to change dir */ + if (((arg & dir_g_in_bits) != arg) && + (arg & changeable_dir_g)) { + arg &= changeable_dir_g; + /* Clear bits in genconfig to set to input */ + if (arg & (1<<0)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g0dir); + dir_g_in_bits |= (1<<0); + dir_g_out_bits &= ~(1<<0); + } + if ((arg & 0x0000FF00) == 0x0000FF00) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g8_15dir); + dir_g_in_bits |= 0x0000FF00; + dir_g_out_bits &= ~0x0000FF00; + } + if ((arg & 0x00FF0000) == 0x00FF0000) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g16_23dir); + dir_g_in_bits |= 0x00FF0000; + dir_g_out_bits &= ~0x00FF0000; + } + if (arg & (1<<24)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g24dir); + dir_g_in_bits |= (1<<24); + dir_g_out_bits &= ~(1<<24); + } + 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); + *R_GEN_CONFIG = genconfig_shadow; + /* Must be a >120 ns delay before writing this again */ + + } + return dir_g_in_bits; + } + return 0; +} /* setget_input */ + +unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) +{ + unsigned long flags; + if (USE_PORTS(priv)) { + local_irq_save(flags); local_irq_disable(); + *priv->dir = *priv->dir_shadow |= + ((unsigned char)arg & priv->changeable_dir); + local_irq_restore(flags); + return *priv->dir_shadow; + } else if (priv->minor == GPIO_MINOR_G) { + /* We must fiddle with R_GEN_CONFIG to change dir */ + if (((arg & dir_g_out_bits) != arg) && + (arg & changeable_dir_g)) { + /* Set bits in genconfig to set to output */ + if (arg & (1<<0)) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g0dir); + dir_g_out_bits |= (1<<0); + dir_g_in_bits &= ~(1<<0); + } + if ((arg & 0x0000FF00) == 0x0000FF00) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g8_15dir); + dir_g_out_bits |= 0x0000FF00; + dir_g_in_bits &= ~0x0000FF00; + } + if ((arg & 0x00FF0000) == 0x00FF0000) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g16_23dir); + dir_g_out_bits |= 0x00FF0000; + dir_g_in_bits &= ~0x00FF0000; + } + if (arg & (1<<24)) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g24dir); + dir_g_out_bits |= (1<<24); + dir_g_in_bits &= ~(1<<24); + } + 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); + *R_GEN_CONFIG = genconfig_shadow; + /* Must be a >120 ns delay before writing this again */ + } + return dir_g_out_bits & 0x7FFFFFFF; + } + return 0; +} /* setget_output */ + +static int +gpio_leds_ioctl(unsigned int cmd, unsigned long arg); + +static int +gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + unsigned long val; + struct gpio_private *priv = (struct gpio_private *)file->private_data; + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { + return -EINVAL; + } + + switch (_IOC_NR(cmd)) { + case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ + // read the port + if (USE_PORTS(priv)) { + return *priv->port; + } else if (priv->minor == GPIO_MINOR_G) { + return (*R_PORT_G_DATA) & 0x7FFFFFFF; + } + break; + case IO_SETBITS: + local_irq_save(flags); local_irq_disable(); + // set changeable bits with a 1 in arg + if (USE_PORTS(priv)) { + *priv->port = *priv->shadow |= + ((unsigned char)arg & priv->changeable_bits); + } else if (priv->minor == GPIO_MINOR_G) { + *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits); + } + local_irq_restore(flags); + break; + case IO_CLRBITS: + local_irq_save(flags); local_irq_disable(); + // clear changeable bits with a 1 in arg + if (USE_PORTS(priv)) { + *priv->port = *priv->shadow &= + ~((unsigned char)arg & priv->changeable_bits); + } else if (priv->minor == GPIO_MINOR_G) { + *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits); + } + local_irq_restore(flags); + break; + case IO_HIGHALARM: + // set alarm when bits with 1 in arg go high + priv->highalarm |= arg; + gpio_some_alarms = 1; + break; + case IO_LOWALARM: + // set alarm when bits with 1 in arg go low + priv->lowalarm |= arg; + gpio_some_alarms = 1; + break; + case IO_CLRALARM: + // clear alarm for bits with 1 in arg + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + break; + case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ + /* Read direction 0=input 1=output */ + if (USE_PORTS(priv)) { + return *priv->dir_shadow; + } else if (priv->minor == GPIO_MINOR_G) { + /* Note: Some bits are both in and out, + * Those that are dual is set here as well. + */ + return (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF; + } + case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ + /* Set direction 0=unchanged 1=input, + * return mask with 1=input + */ + return setget_input(priv, arg) & 0x7FFFFFFF; + break; + case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ + /* Set direction 0=unchanged 1=output, + * return mask with 1=output + */ + return setget_output(priv, arg) & 0x7FFFFFFF; + + case IO_SHUTDOWN: + SOFT_SHUTDOWN(); + break; + case IO_GET_PWR_BT: +#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN) + return (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT)); +#else + return 0; +#endif + break; + case IO_CFG_WRITE_MODE: + priv->clk_mask = arg & 0xFF; + priv->data_mask = (arg >> 8) & 0xFF; + priv->write_msb = (arg >> 16) & 0x01; + /* Check if we're allowed to change the bits and + * the direction is correct + */ + if (!((priv->clk_mask & priv->changeable_bits) && + (priv->data_mask & priv->changeable_bits) && + (priv->clk_mask & *priv->dir_shadow) && + (priv->data_mask & *priv->dir_shadow))) + { + priv->clk_mask = 0; + priv->data_mask = 0; + return -EPERM; + } + break; + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + if (USE_PORTS(priv)) { + val = *priv->port; + } else if (priv->minor == GPIO_MINOR_G) { + val = *R_PORT_G_DATA; + } + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + break; + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + if (USE_PORTS(priv)) { + val = *priv->shadow; + } else if (priv->minor == GPIO_MINOR_G) { + val = port_g_data_shadow; + } + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_INPUT: + /* bits set in *arg is set to input, + * *arg updated with current input pins. + */ + if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_OUTPUT: + /* bits set in *arg is set to output, + * *arg updated with current output pins. + */ + if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + if (priv->minor == GPIO_MINOR_LEDS) + return gpio_leds_ioctl(cmd, arg); + else + return -EINVAL; + } /* switch */ + + return 0; +} + +static int +gpio_leds_ioctl(unsigned int cmd, unsigned long arg) +{ + unsigned char green; + unsigned char red; + + switch (_IOC_NR(cmd)) { + case IO_LEDACTIVE_SET: + green = ((unsigned char) arg) & 1; + red = (((unsigned char) arg) >> 1) & 1; + LED_ACTIVE_SET_G(green); + LED_ACTIVE_SET_R(red); + break; + + case IO_LED_SETBIT: + LED_BIT_SET(arg); + break; + + case IO_LED_CLRBIT: + LED_BIT_CLR(arg); + break; + + default: + return -EINVAL; + } /* switch */ + + return 0; +} + +struct file_operations gpio_fops = { + .owner = THIS_MODULE, + .poll = gpio_poll, + .ioctl = gpio_ioctl, + .write = gpio_write, + .open = gpio_open, + .release = gpio_release, +}; + + +static void __init gpio_init_port_g(void) +{ +#define GROUPA (0x0000FF3F) +#define GROUPB (1<<6 | 1<<7) +#define GROUPC (1<<30 | 1<<31) +#define GROUPD (0x3FFF0000) +#define GROUPD_LOW (0x00FF0000) + unsigned long used_in_bits = 0; + unsigned long used_out_bits = 0; + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi0, select)){ + used_in_bits |= GROUPA | GROUPB | 0 | 0; + used_out_bits |= GROUPA | GROUPB | 0 | 0; + } + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ata, select)) { + used_in_bits |= GROUPA | GROUPB | GROUPC | (GROUPD & ~(1<<25|1<<26)); + used_out_bits |= GROUPA | GROUPB | GROUPC | GROUPD; + } + + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, par0, select)) { + used_in_bits |= (GROUPA & ~(1<<0)) | 0 | 0 | 0; + used_out_bits |= (GROUPA & ~(1<<0)) | 0 | 0 | 0; + } + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ser2, select)) { + used_in_bits |= 0 | GROUPB | 0 | 0; + used_out_bits |= 0 | GROUPB | 0 | 0; + } + /* mio same as shared RAM ? */ + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, mio, select)) { + used_in_bits |= (GROUPA & ~(1<<0)) | 0 |0 |GROUPD_LOW; + used_out_bits |= (GROUPA & ~(1<<0|1<<1|1<<2)) | 0 |0 |GROUPD_LOW; + } + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi1, select)) { + used_in_bits |= 0 | 0 | GROUPC | GROUPD; + used_out_bits |= 0 | 0 | GROUPC | GROUPD; + } + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi0w, select)) { + used_in_bits |= GROUPA | GROUPB | 0 | (GROUPD_LOW | 1<<24); + used_out_bits |= GROUPA | GROUPB | 0 | (GROUPD_LOW | 1<<24 | 1<<25|1<<26); + } + + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, par1, select)) { + used_in_bits |= 0 | 0 | 0 | (GROUPD & ~(1<<24)); + used_out_bits |= 0 | 0 | 0 | (GROUPD & ~(1<<24)); + } + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ser3, select)) { + used_in_bits |= 0 | 0 | GROUPC | 0; + used_out_bits |= 0 | 0 | GROUPC | 0; + } + /* mio same as shared RAM-W? */ + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, mio_w, select)) { + used_in_bits |= (GROUPA & ~(1<<0)) | 0 | 0 |GROUPD_LOW; + used_out_bits |= (GROUPA & ~(1<<0|1<<1|1<<2)) | 0 | 0 |GROUPD_LOW; + } + /* TODO: USB p2, parw, sync ser3? */ + + /* Initialise the dir_g_shadow etc. depending on genconfig */ + /* 0=input 1=output */ + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out)) + dir_g_shadow |= (1 << 0); + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out)) + dir_g_shadow |= 0x0000FF00; + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g16_23dir, out)) + dir_g_shadow |= 0x00FF0000; + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out)) + dir_g_shadow |= (1 << 24); + + dir_g_in_bits = ~used_in_bits; + dir_g_out_bits = ~used_out_bits; + + changeable_dir_g = 0x01FFFF01; /* all that can change dir */ + changeable_dir_g &= dir_g_out_bits; + changeable_dir_g &= dir_g_in_bits; + /* Correct the bits that can change direction */ + dir_g_out_bits &= ~changeable_dir_g; + dir_g_out_bits |= dir_g_shadow; + dir_g_in_bits &= ~changeable_dir_g; + dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g); + + + printk("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", + dir_g_shadow, changeable_dir_g); +} + +/* main driver initialization routine, called from mem.c */ + +static __init int +gpio_init(void) +{ + int res; +#if defined (CONFIG_ETRAX_CSP0_LEDS) + int i; +#endif + + /* do the formalities */ + + res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); + if (res < 0) { + printk(KERN_ERR "gpio: couldn't get a major number.\n"); + return res; + } + + /* Clear all leds */ +#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) + LED_NETWORK_SET(0); + LED_ACTIVE_SET(0); + LED_DISK_READ(0); + LED_DISK_WRITE(0); + +#if defined (CONFIG_ETRAX_CSP0_LEDS) + for (i = 0; i < 32; i++) { + LED_BIT_SET(i); + } +#endif + +#endif + gpio_init_port_g(); + printk("ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002 Axis Communications AB\n"); + /* We call etrax_gpio_wake_up_check() from timer interrupt and + * from cpu_idle() in kernel/process.c + * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms + * in some tests. + */ + if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, + SA_SHIRQ | SA_INTERRUPT,"gpio poll", NULL)) { + printk("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"); + } + + + return res; +} + +/* this makes sure that gpio_init is called during kernel boot */ + +module_init(gpio_init); diff --git a/arch/cris/drivers/i2c.c b/arch/cris/arch-v10/drivers/i2c.c index 093c5d5dac13..7731f7d22f91 100644 --- a/arch/cris/drivers/i2c.c +++ b/arch/cris/arch-v10/drivers/i2c.c @@ -12,8 +12,25 @@ *! don't use PB_I2C if DS1302 uses same bits, *! use PB. *! $Log: i2c.c,v $ -*! Revision 1.1.1.1 2001/12/17 13:59:27 bjornw -*! Import of Linux 2.5.1 +*! Revision 1.4 2002/12/11 13:13:57 starvik +*! Added arch/ to v10 specific includes +*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) +*! +*! Revision 1.3 2002/11/20 11:56:11 starvik +*! Merge of Linux 2.5.48 +*! +*! Revision 1.2 2002/11/18 13:16:06 starvik +*! Linux 2.5 port of latest 2.4 drivers +*! +*! Revision 1.9 2002/10/31 15:32:26 starvik +*! Update Port B register and shadow even when running with hardware support +*! to avoid glitches when reading bits +*! Never set direction to out in i2c_inbyte +*! Removed incorrect clock togling at end of i2c_inbyte +*! +*! Revision 1.8 2002/08/13 06:31:53 starvik +*! Made SDA and SCL line configurable +*! Modified i2c_inbyte to work with PCF8563 *! *! Revision 1.7 2001/04/04 13:11:36 markusl *! Updated according to review remarks @@ -43,10 +60,11 @@ *! *! --------------------------------------------------------------------------- *! -*! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN +*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: i2c.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ */ +/* $Id: i2c.c,v 1.4 2002/12/11 13:13:57 starvik Exp $ */ + /****************** INCLUDE FILES SECTION ***********************************/ #include <linux/module.h> @@ -62,7 +80,7 @@ #include <asm/etraxi2c.h> #include <asm/system.h> -#include <asm/svinto.h> +#include <asm/arch/svinto.h> #include <asm/io.h> #include <asm/delay.h> @@ -96,8 +114,15 @@ static const char i2c_name[] = "i2c"; #ifdef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C /* Use PB and not PB_I2C */ -#define SDABIT 0 -#define SCLBIT 1 +#ifndef CONFIG_ETRAX_I2C_DATA_PORT +#define CONFIG_ETRAX_I2C_DATA_PORT 0 +#endif +#ifndef CONFIG_ETRAX_I2C_CLK_PORT +#define CONFIG_ETRAX_I2C_CLK_PORT 1 +#endif + +#define SDABIT CONFIG_ETRAX_I2C_DATA_PORT +#define SCLBIT CONFIG_ETRAX_I2C_CLK_PORT #define i2c_enable() #define i2c_disable() @@ -117,7 +142,7 @@ static const char i2c_name[] = "i2c"; /* read a bit from the i2c interface */ -#define i2c_getbit() (*R_PORT_PB_READ & (1 << SDABIT)) +#define i2c_getbit() (((*R_PORT_PB_READ & (1 << SDABIT))) >> SDABIT) #else /* enable or disable the i2c interface */ @@ -127,16 +152,24 @@ static const char i2c_name[] = "i2c"; /* enable or disable output-enable, to select output or input on the i2c bus */ -#define i2c_dir_out() *R_PORT_PB_I2C = (port_pb_i2c_shadow &= ~IO_MASK(R_PORT_PB_I2C, i2c_oe_)) -#define i2c_dir_in() *R_PORT_PB_I2C = (port_pb_i2c_shadow |= IO_MASK(R_PORT_PB_I2C, i2c_oe_)) +#define i2c_dir_out() \ + *R_PORT_PB_I2C = (port_pb_i2c_shadow &= ~IO_MASK(R_PORT_PB_I2C, i2c_oe_)); \ + REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, 0, 1); +#define i2c_dir_in() \ + *R_PORT_PB_I2C = (port_pb_i2c_shadow |= IO_MASK(R_PORT_PB_I2C, i2c_oe_)); \ + REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, 0, 0); /* control the i2c clock and data signals */ -#define i2c_clk(x) *R_PORT_PB_I2C = (port_pb_i2c_shadow = (port_pb_i2c_shadow & \ - ~IO_MASK(R_PORT_PB_I2C, i2c_clk)) | IO_FIELD(R_PORT_PB_I2C, i2c_clk, (x))) +#define i2c_clk(x) \ + *R_PORT_PB_I2C = (port_pb_i2c_shadow = (port_pb_i2c_shadow & \ + ~IO_MASK(R_PORT_PB_I2C, i2c_clk)) | IO_FIELD(R_PORT_PB_I2C, i2c_clk, (x))); \ + REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 1, x); -#define i2c_data(x) *R_PORT_PB_I2C = (port_pb_i2c_shadow = (port_pb_i2c_shadow & \ - ~IO_MASK(R_PORT_PB_I2C, i2c_d)) | IO_FIELD(R_PORT_PB_I2C, i2c_d, (x))) +#define i2c_data(x) \ + *R_PORT_PB_I2C = (port_pb_i2c_shadow = (port_pb_i2c_shadow & \ + ~IO_MASK(R_PORT_PB_I2C, i2c_d)) | IO_FIELD(R_PORT_PB_I2C, i2c_d, (x))); \ + REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 0, x); /* read a bit from the i2c interface */ @@ -209,14 +242,15 @@ void i2c_outbyte(unsigned char x) { int i; - + i2c_dir_out(); for (i = 0; i < 8; i++) { - if (x & 0x80) + if (x & 0x80) { i2c_data(I2C_DATA_HIGH); - else + } else { i2c_data(I2C_DATA_LOW); + } i2c_delay(CLOCK_LOW_TIME/2); i2c_clk(I2C_CLOCK_HIGH); @@ -241,65 +275,41 @@ i2c_inbyte(void) { unsigned char aBitByte = 0; int i; - int iaa; - /* - * enable output - */ - i2c_dir_out(); - /* - * Release data bus by setting - * data high - */ - i2c_data(I2C_DATA_HIGH); - /* - * enable input - */ + /* Switch off I2C to get bit */ + i2c_disable(); i2c_dir_in(); - /* - * Use PORT PB instead of I2C - * for input. (I2C not working) - */ - i2c_clk(1); - i2c_data(1); - /* - * get bits - */ - for (i = 0; i < 8; i++) { - i2c_delay(CLOCK_LOW_TIME/2); - /* - * low clock period - */ + i2c_delay(CLOCK_HIGH_TIME/2); + + /* Get bit */ + aBitByte |= i2c_getbit(); + + /* Enable I2C */ + i2c_enable(); + i2c_delay(CLOCK_LOW_TIME/2); + + for (i = 1; i < 8; i++) { + aBitByte <<= 1; + /* Clock pulse */ i2c_clk(I2C_CLOCK_HIGH); - /* - * switch off I2C - */ - i2c_data(1); + i2c_delay(CLOCK_HIGH_TIME); + i2c_clk(I2C_CLOCK_LOW); + i2c_delay(CLOCK_LOW_TIME); + + /* Switch off I2C to get bit */ i2c_disable(); i2c_dir_in(); - /* - * wait before getting bit - */ - i2c_delay(CLOCK_HIGH_TIME/2); - aBitByte = (aBitByte << 1); - iaa = i2c_getbit(); - aBitByte = aBitByte | iaa ; - /* - * wait - */ i2c_delay(CLOCK_HIGH_TIME/2); - /* - * end clock puls - */ + + /* Get bit */ + aBitByte |= i2c_getbit(); + + /* Enable I2C */ i2c_enable(); - i2c_dir_out(); - i2c_clk(I2C_CLOCK_LOW); - /* - * low clock period - */ i2c_delay(CLOCK_LOW_TIME/2); } - i2c_dir_out(); + i2c_clk(I2C_CLOCK_HIGH); + i2c_delay(CLOCK_HIGH_TIME); return aBitByte; } @@ -424,44 +434,25 @@ i2c_sendack(void) *# *#--------------------------------------------------------------------------*/ int -i2c_writereg(unsigned char theSlave, unsigned char theReg, +i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue) { int error, cntr = 3; unsigned long flags; - + do { error = 0; /* * we don't like to be interrupted */ - save_flags(flags); - cli(); - /* - * generate start condition - */ - i2c_start(); - /* - * dummy preamble - */ - i2c_outbyte(0x01); - i2c_data(I2C_DATA_HIGH); - i2c_clk(I2C_CLOCK_HIGH); - i2c_delay(CLOCK_HIGH_TIME); /* Dummy Acknowledge */ - i2c_clk(I2C_CLOCK_LOW); - i2c_delay(CLOCK_LOW_TIME); - i2c_clk(I2C_CLOCK_HIGH); - i2c_delay(CLOCK_LOW_TIME); /* Repeated Start Condition */ - i2c_data(I2C_DATA_LOW); - i2c_delay(CLOCK_HIGH_TIME); - i2c_clk(I2C_CLOCK_LOW); - i2c_delay(CLOCK_LOW_TIME); + local_irq_save(flags); + local_irq_disable(); i2c_start(); /* * send slave address */ - i2c_outbyte(theSlave); + i2c_outbyte((theSlave & 0xfe)); /* * wait for ack */ @@ -493,10 +484,10 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, /* * enable interrupt again */ - restore_flags(flags); + local_irq_restore(flags); } while(error && cntr--); - + i2c_delay(CLOCK_LOW_TIME); return -error; @@ -515,40 +506,23 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) unsigned char b = 0; int error, cntr = 3; unsigned long flags; - + do { error = 0; /* * we don't like to be interrupted */ - save_flags(flags); - cli(); + local_irq_save(flags); + local_irq_disable(); /* * generate start condition */ i2c_start(); - /* - * dummy preamble - */ - i2c_outbyte(0x01); - i2c_data(I2C_DATA_HIGH); - i2c_clk(I2C_CLOCK_HIGH); - i2c_delay(CLOCK_HIGH_TIME); /* Dummy Acknowledge */ - i2c_clk(I2C_CLOCK_LOW); - i2c_delay(CLOCK_LOW_TIME); - i2c_clk(I2C_CLOCK_HIGH); - i2c_delay(CLOCK_LOW_TIME); /* Repeated Start Condition */ - i2c_data(I2C_DATA_LOW); - i2c_delay(CLOCK_HIGH_TIME); - i2c_clk(I2C_CLOCK_LOW); - i2c_delay(CLOCK_LOW_TIME); - - i2c_start(); /* * send slave address */ - i2c_outbyte(theSlave); + i2c_outbyte((theSlave & 0xfe)); /* * wait for ack */ @@ -593,7 +567,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) /* * enable interrupt again */ - restore_flags(flags); + local_irq_restore(flags); } while(error && cntr--); diff --git a/arch/cris/drivers/i2c.h b/arch/cris/arch-v10/drivers/i2c.h index d789274adc71..d0caa9ff883e 100644 --- a/arch/cris/drivers/i2c.h +++ b/arch/cris/arch-v10/drivers/i2c.h @@ -1,4 +1,4 @@ -/* $Id: i2c.h,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ */ +/* $Id: i2c.h,v 1.2 2002/11/18 13:16:06 starvik Exp $ */ /* High level I2C actions */ int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue); diff --git a/arch/cris/arch-v10/drivers/pcf8563.c b/arch/cris/arch-v10/drivers/pcf8563.c new file mode 100644 index 000000000000..6a677bce6f14 --- /dev/null +++ b/arch/cris/arch-v10/drivers/pcf8563.c @@ -0,0 +1,287 @@ +/* + * PCF8563 RTC + * + * From Phillips' datasheet: + * + * The PCF8563 is a CMOS real-time clock/calendar optimized for low power + * consumption. A programmable clock output, interupt output and voltage + * low detector are also provided. All address and data are transferred + * serially via two-line bidirectional I2C-bus. Maximum bus speed is + * 400 kbits/s. The built-in word address register is incremented + * automatically after each written or read bute. + * + * Copyright (c) 2002, Axis Communications AB + * All rights reserved. + * + * Author: Tobias Anderberg <tobiasa@axis.com>. + * + * $Id: pcf8563.c,v 1.1 2002/12/12 08:27:26 starvik Exp $ + */ + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <linux/delay.h> + +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/arch/svinto.h> +#include <asm/rtc.h> +#include "i2c.h" + +#define PCF8563_MAJOR 121 /* Local major number. */ +#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ +#define PCF8563_NAME "PCF8563" +#define DRIVER_VERSION "$Revision: 1.1 $" + +/* I2C bus slave registers. */ +#define RTC_I2C_READ 0xa3 +#define RTC_I2C_WRITE 0xa2 + +/* Two simple wrapper macros, saves a few keystrokes. */ +#define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) +#define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) + +static const unsigned char days_in_month[] = + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +int pcf8563_open(struct inode *, struct file *); +int pcf8563_release(struct inode *, struct file *); + +static struct file_operations pcf8563_fops = { + owner: THIS_MODULE, + ioctl: pcf8563_ioctl, + open: pcf8563_open, + release: pcf8563_release, +}; + +unsigned char +pcf8563_readreg(int reg) +{ + unsigned char res = i2c_readreg(RTC_I2C_READ, reg); + + /* The PCF8563 does not return 0 for unimplemented bits */ + switch(reg) + { + case RTC_SECONDS: + case RTC_MINUTES: + res &= 0x7f; + break; + case RTC_HOURS: + case RTC_DAY_OF_MONTH: + res &= 0x3f; + break; + case RTC_MONTH: + res = (res & 0x1f) - 1; /* PCF8563 returns month in range 1-12 */ + break; + } + return res; +} + +void +pcf8563_writereg(int reg, unsigned char val) +{ + i2c_writereg(RTC_I2C_WRITE,reg,val); +} + +void +get_rtc_time(struct rtc_time *tm) +{ + tm->tm_sec = rtc_read(RTC_SECONDS); + tm->tm_min = rtc_read(RTC_MINUTES); + tm->tm_hour = rtc_read(RTC_HOURS); + tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH); + tm->tm_mon = rtc_read(RTC_MONTH); + tm->tm_year = rtc_read(RTC_YEAR); + + if (tm->tm_sec & 0x80) + printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); + + tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); + tm->tm_sec &= 0x7f; + tm->tm_min &= 0x7f; + tm->tm_hour &= 0x3f; + tm->tm_mday &= 0x3f; + tm->tm_mon &= 0x1f; + + BCD_TO_BIN(tm->tm_sec); + BCD_TO_BIN(tm->tm_min); + BCD_TO_BIN(tm->tm_hour); + BCD_TO_BIN(tm->tm_mday); + BCD_TO_BIN(tm->tm_mon); + tm->tm_mon--; /* Month is 1..12 in RTC but 0..11 in linux */ +} + +int __init +pcf8563_init(void) +{ + unsigned char ret; + /* + * First of all we need to reset the chip. This is done by + * clearing control1, control2 and clk freq, clear the + * Voltage Low bit, and resetting all alarms. + */ + if (rtc_write(RTC_CONTROL1, 0x00) < 0) + goto err; + + if (rtc_write(RTC_CONTROL2, 0x00) < 0) + goto err; + + if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0) + goto err; + + /* Clear the VL bit in the seconds register. */ + ret = rtc_read(RTC_SECONDS); + + if (rtc_write(RTC_SECONDS, (ret & 0x7f)) < 0) + goto err; + + /* Reset the alarms. */ + if (rtc_write(RTC_MINUTE_ALARM, 0x00) < 0) + goto err; + + if (rtc_write(RTC_HOUR_ALARM, 0x00) < 0) + goto err; + + if (rtc_write(RTC_DAY_ALARM, 0x00) < 0) + goto err; + + if (rtc_write(RTC_WEEKDAY_ALARM, 0x00) < 0) + goto err; + + if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { + printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n", + PCF8563_NAME, PCF8563_MAJOR); + return -1; + } + + printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); + + /* Check for low voltage, and warn about it.. */ + if (rtc_read(RTC_SECONDS) & 0x80) + printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); + + return 0; + +err: + printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); + return -1; +} + +void __exit +pcf8563_exit(void) +{ + if (unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME) < 0) { + printk(KERN_INFO "%s: Unable to unregister device.\n", PCF8563_NAME); + } +} + +/* + * ioctl calls for this driver. Why return -ENOTTY upon error? Because + * POSIX says so! + */ +int +pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + /* Some sanity checks. */ + if (_IOC_TYPE(cmd) != RTC_MAGIC) + return -ENOTTY; + + if (_IOC_NR(cmd) > RTC_MAX_IOCTL) + return -ENOTTY; + + switch (cmd) { + case RTC_RD_TIME: + { + struct rtc_time tm; + + get_rtc_time(&tm); + + if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) { + return -EFAULT; + } + + return 0; + } + break; + case RTC_SET_TIME: + { + 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; + } + break; + default: + return -ENOTTY; + } + + return 0; +} + +int +pcf8563_open(struct inode *inode, struct file *filp) +{ + MOD_INC_USE_COUNT; + return 0; +} + +int +pcf8563_release(struct inode *inode, struct file *filp) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +EXPORT_NO_SYMBOLS; +module_init(pcf8563_init); +module_exit(pcf8563_exit); diff --git a/arch/cris/drivers/serial.c b/arch/cris/arch-v10/drivers/serial.c index 1a47d686061f..2822ffb812cc 100644 --- a/arch/cris/drivers/serial.c +++ b/arch/cris/arch-v10/drivers/serial.c @@ -1,13 +1,129 @@ -/* $Id: serial.c,v 1.3 2001/12/19 10:32:35 johana Exp $ +/* $Id: serial.c,v 1.17 2003/07/04 08:27:37 starvik Exp $ * * Serial port driver for the ETRAX 100LX chip * - * Copyright (C) 1998, 1999, 2000, 2001 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.3 2001/12/19 10:32:35 johana + * Revision 1.17 2003/07/04 08:27:37 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.16 2003/06/13 10:05:19 johana + * Help the user to avoid trouble by: + * Forcing mixed mode for status/control lines if not all pins are used. + * + * Revision 1.15 2003/06/13 09:43:01 johana + * Merged in the following changes from os/linux/arch/cris/drivers/serial.c + * + some minor changes to reduce diff. + * + * 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.14 2003/04/09 08:12:44 pkj + * Corrected typo changes made upstream. + * + * Revision 1.13 2003/04/09 05:20:47 starvik + * Merge of Linux 2.5.67 + * + * Revision 1.11 2003/01/22 06:48:37 starvik + * Fixed warnings issued by GCC 3.2.1 + * + * Revision 1.9 2002/12/13 09:07:47 starvik + * Alert user that RX_TIMEOUT_TICKS==0 doesn't work + * + * Revision 1.8 2002/12/11 13:13:57 starvik + * Added arch/ to v10 specific includes + * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) + * + * Revision 1.7 2002/12/06 07:13:57 starvik + * Corrected work queue stuff + * Removed CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST + * + * Revision 1.6 2002/11/21 07:17:46 starvik + * Change static inline to extern inline where otherwise outlined with gcc-3.2 + * + * Revision 1.5 2002/11/14 15:59:49 starvik + * Linux 2.5 port of the latest serial driver from 2.4. The work queue stuff + * probably doesn't work yet. + * + * Revision 1.42 2002/11/05 09:08:47 johana + * Better implementation of rs_stop() and rs_start() that uses the XOFF + * register to start/stop transmission. + * change_speed() also initilises XOFF register correctly so that + * auto_xoff is enabled when IXON flag is set by user. + * This gives fast XOFF response times. + * + * Revision 1.41 2002/11/04 18:40:57 johana + * Implemented rs_stop() and rs_start(). + * Simple tests using hwtestserial indicates that this should be enough + * to make it work. + * + * Revision 1.40 2002/10/14 05:33:18 starvik + * RS-485 uses fast timers even if SERIAL_FAST_TIMER is disabled + * + * Revision 1.39 2002/09/30 21:00:57 johana + * Support for CONFIG_ETRAX_SERx_DTR_RI_DSR_CD_MIXED where the status and + * control pins can be mixed between PA and PB. + * If no serial port uses MIXED old solution is used + * (saves a few bytes and cycles). + * control_pins struct uses masks instead of bit numbers. + * Corrected dummy values and polarity in line_info() so + * /proc/tty/driver/serial is now correct. + * (the E100_xxx_GET() macros is really active low - perhaps not obvious) + * + * Revision 1.38 2002/08/23 11:01:36 starvik + * Check that serial port is enabled in all interrupt handlers to avoid + * restarts of DMA channels not assigned to serial ports + * + * Revision 1.37 2002/08/13 13:02:37 bjornw + * Removed some warnings because of unused code + * + * Revision 1.36 2002/08/08 12:50:01 starvik + * Serial interrupt is shared with synchronous serial port driver + * + * Revision 1.35 2002/06/03 10:40:49 starvik + * Increased RS-485 RTS toggle timer to 2 characters + * + * Revision 1.34 2002/05/28 18:59:36 johana + * Whitespace and comment fixing to be more like etrax100ser.c 1.71. + * + * Revision 1.33 2002/05/28 17:55:43 johana + * RS-485 uses FAST_TIMER if enabled, and starts a short (one char time) + * timer from tranismit_chars (interrupt context). + * The timer toggles RTS in interrupt context when expired giving minimum + * latencies. + * + * Revision 1.32 2002/05/22 13:58:00 johana + * Renamed rs_write() to raw_write() and made it inline. + * New rs_write() handles RS-485 if configured and enabled + * (moved code from e100_write_rs485()). + * RS-485 ioctl's uses copy_from_user() instead of verify_area(). + * + * Revision 1.31 2002/04/22 11:20:03 johana + * Updated copyright years. + * + * Revision 1.30 2002/04/22 09:39:12 johana + * RS-485 support compiles. + * + * Revision 1.29 2002/01/14 16:10:01 pkj + * Allocate the receive buffers dynamically. The static 4kB buffer was + * too small for the peaks. This means that we can get rid of the extra + * buffer and the copying to it. It also means we require less memory + * under normal operations, but can use more when needed (there is a + * cap at 64kB for safety reasons). If there is no memory available + * we panic(), and die a horrible death... + * + * Revision 1.28 2001/12/18 15:04:53 johana * Cleaned up write_rs485() - now it works correctly without padding extra * char. * Added sane default initialisation of rs485. @@ -283,7 +399,7 @@ * */ -static char *serial_version = "$Revision: 1.3 $"; +static char *serial_version = "$Revision: 1.17 $"; #include <linux/config.h> #include <linux/version.h> @@ -301,12 +417,8 @@ static char *serial_version = "$Revision: 1.3 $"; #include <linux/fcntl.h> #include <linux/mm.h> #include <linux/slab.h> -#if (LINUX_VERSION_CODE >= 131343) #include <linux/init.h> -#endif -#if (LINUX_VERSION_CODE >= 131336) #include <asm/uaccess.h> -#endif #include <linux/kernel.h> #include <asm/io.h> @@ -314,28 +426,38 @@ static char *serial_version = "$Revision: 1.3 $"; #include <asm/system.h> #include <asm/segment.h> #include <asm/bitops.h> -#include <asm/delay.h> +#include <linux/delay.h> -#include <asm/svinto.h> +#include <asm/arch/svinto.h> /* non-arch dependent serial structures are in linux/serial.h */ #include <linux/serial.h> /* while we keep our own stuff (struct e100_serial) in a local .h file */ #include "serial.h" +#include <asm/fasttimer.h> + +#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER +#ifndef CONFIG_ETRAX_FAST_TIMER +#error "Enable FAST_TIMER to use SERIAL_FAST_TIMER" +#endif +#endif + +#if defined(CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS) && \ + (CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS == 0) +#error "RX_TIMEOUT_TICKS == 0 not allowed, use 1" +#endif /* * All of the compatibilty code so we can compile serial.c against * older kernels is hidden in serial_compat.h */ -#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */ +#if defined(LOCAL_HEADERS) #include "serial_compat.h" #endif #define _INLINE_ inline -static DECLARE_TASK_QUEUE(tq_serial); - -static struct tty_driver *serial_driver; +struct tty_driver *serial_driver; /* serial subtype definitions */ #ifndef SERIAL_TYPE_NORMAL @@ -361,8 +483,7 @@ static struct tty_driver *serial_driver; #define TTY_THROTTLE_LIMIT (TTY_FLIPBUF_SIZE/10) -#define SERIAL_RECV_SIZE 4096 -#define SERIAL_DESCR_BUF_SIZE 512 +#define SERIAL_DESCR_BUF_SIZE 256 /* Add an x here to log a lot of timer stuff */ #define TIMERD(x) @@ -381,6 +502,14 @@ static void change_speed(struct e100_serial *info); 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); +static inline int 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); + #define DEF_BAUD 0x99 /* 115.2 kbit/s */ #define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) @@ -487,6 +616,9 @@ static struct e100_serial rs_table[] = { #define NR_PORTS (sizeof(rs_table)/sizeof(struct e100_serial)) +#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER +static struct fast_timer fast_timers[NR_PORTS]; +#endif #ifdef CONFIG_ETRAX_SERIAL_PROC_ENTRY #define PROCSTAT(x) x @@ -506,7 +638,7 @@ struct ser_statistics_type { static struct ser_statistics_type ser_stat[NR_PORTS]; -#else +#else #define PROCSTAT(x) @@ -514,91 +646,440 @@ static struct ser_statistics_type ser_stat[NR_PORTS]; /* RS-485 */ #if defined(CONFIG_ETRAX_RS485) +#ifdef CONFIG_ETRAX_FAST_TIMER +static struct fast_timer fast_timers_rs485[NR_PORTS]; +#endif #if defined(CONFIG_ETRAX_RS485_ON_PA) static int rs485_pa_bit = CONFIG_ETRAX_RS485_ON_PA_BIT; #endif #endif - -/* For now we assume that all bits are on the same port for each serial port */ +/* Info and macros needed for each ports extra control/status signals. */ +#define E100_STRUCT_PORT(line, pinname) \ + ((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])) + +#define E100_STRUCT_SHADOW(line, pinname) \ + ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \ + (&port_pa_data_shadow): ( \ + (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \ + (&port_pb_data_shadow):&dummy_ser[line])) +#define E100_STRUCT_MASK(line, pinname) \ + ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \ + (1<<CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT): ( \ + (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \ + (1<<CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT):DUMMY_##pinname##_MASK)) + +#define DUMMY_DTR_MASK 1 +#define DUMMY_RI_MASK 2 +#define DUMMY_DSR_MASK 4 +#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 -/* Dummy shadow variables */ -#if !defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB) -static unsigned char dummy_ser0 = 0x00; -static unsigned char dummy_dir_ser0 = 0x00; -#endif -#if !defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB) -static unsigned char dummy_ser1 = 0x00; -static unsigned char dummy_dir_ser1 = 0x00; -#endif -#if !defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA) -static unsigned char dummy_ser2 = 0x00; -static unsigned char dummy_dir_ser2 = 0x00; -#endif +#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 SER0_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 */ -static unsigned char dummy_ser3 = 0x00; -static unsigned char dummy_dir_ser3 = 0x00; +#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) || \ + defined(CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED) +#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) \ + &dummy_ser[line], &dummy_ser[line], \ + &dummy_ser[line], &dummy_ser[line], \ + &dummy_ser[line], &dummy_ser[line], \ + &dummy_ser[line], &dummy_ser[line], \ + DUMMY_DTR_MASK, DUMMY_RI_MASK, DUMMY_DSR_MASK, DUMMY_CD_MASK + + +struct control_pins +{ + volatile unsigned char *dtr_port; + unsigned char *dtr_shadow; + volatile unsigned char *ri_port; + unsigned char *ri_shadow; + volatile unsigned char *dsr_port; + unsigned char *dsr_shadow; + volatile unsigned char *cd_port; + unsigned char *cd_shadow; + + unsigned char dtr_mask; + unsigned char ri_mask; + unsigned char dsr_mask; + unsigned char cd_mask; +}; + +static const struct control_pins e100_modem_pins[NR_PORTS] = +{ + /* Ser 0 */ + { +#ifdef CONFIG_ETRAX_SERIAL_PORT0 + E100_STRUCT_PORT(0,DTR), E100_STRUCT_SHADOW(0,DTR), + E100_STRUCT_PORT(0,RI), E100_STRUCT_SHADOW(0,RI), + E100_STRUCT_PORT(0,DSR), E100_STRUCT_SHADOW(0,DSR), + E100_STRUCT_PORT(0,CD), E100_STRUCT_SHADOW(0,CD), + E100_STRUCT_MASK(0,DTR), + E100_STRUCT_MASK(0,RI), + E100_STRUCT_MASK(0,DSR), + E100_STRUCT_MASK(0,CD) +#else + CONTROL_PINS_PORT_NOT_USED(0) +#endif + }, + + /* Ser 1 */ + { +#ifdef CONFIG_ETRAX_SERIAL_PORT1 + E100_STRUCT_PORT(1,DTR), E100_STRUCT_SHADOW(1,DTR), + E100_STRUCT_PORT(1,RI), E100_STRUCT_SHADOW(1,RI), + E100_STRUCT_PORT(1,DSR), E100_STRUCT_SHADOW(1,DSR), + E100_STRUCT_PORT(1,CD), E100_STRUCT_SHADOW(1,CD), + E100_STRUCT_MASK(1,DTR), + E100_STRUCT_MASK(1,RI), + E100_STRUCT_MASK(1,DSR), + E100_STRUCT_MASK(1,CD) +#else + CONTROL_PINS_PORT_NOT_USED(1) +#endif + }, + + /* Ser 2 */ + { +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + E100_STRUCT_PORT(2,DTR), E100_STRUCT_SHADOW(2,DTR), + E100_STRUCT_PORT(2,RI), E100_STRUCT_SHADOW(2,RI), + E100_STRUCT_PORT(2,DSR), E100_STRUCT_SHADOW(2,DSR), + E100_STRUCT_PORT(2,CD), E100_STRUCT_SHADOW(2,CD), + E100_STRUCT_MASK(2,DTR), + E100_STRUCT_MASK(2,RI), + E100_STRUCT_MASK(2,DSR), + E100_STRUCT_MASK(2,CD) +#else + CONTROL_PINS_PORT_NOT_USED(2) +#endif + }, + + /* Ser 3 */ + { +#ifdef CONFIG_ETRAX_SERIAL_PORT3 + E100_STRUCT_PORT(3,DTR), E100_STRUCT_SHADOW(3,DTR), + E100_STRUCT_PORT(3,RI), E100_STRUCT_SHADOW(3,RI), + E100_STRUCT_PORT(3,DSR), E100_STRUCT_SHADOW(3,DSR), + E100_STRUCT_PORT(3,CD), E100_STRUCT_SHADOW(3,CD), + E100_STRUCT_MASK(3,DTR), + E100_STRUCT_MASK(3,RI), + E100_STRUCT_MASK(3,DSR), + E100_STRUCT_MASK(3,CD) +#else + CONTROL_PINS_PORT_NOT_USED(3) +#endif + } +}; +#else /* CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED */ + +/* All pins are on either PA or PB for each serial port */ +#define CONTROL_PINS_PORT_NOT_USED(line) \ + &dummy_ser[line], &dummy_ser[line], \ + DUMMY_DTR_MASK, DUMMY_RI_MASK, DUMMY_DSR_MASK, DUMMY_CD_MASK + -/* Info needed for each ports extra control/status signals. - We only supports that all pins uses same register for each port */ struct control_pins { volatile unsigned char *port; - volatile unsigned char *shadow; - volatile unsigned char *dir_shadow; - - unsigned char dtr_bit; - unsigned char ri_bit; - unsigned char dsr_bit; - unsigned char cd_bit; + unsigned char *shadow; + + unsigned char dtr_mask; + unsigned char ri_mask; + unsigned char dsr_mask; + unsigned char cd_mask; }; +#define dtr_port port +#define dtr_shadow shadow +#define ri_port port +#define ri_shadow shadow +#define dsr_port port +#define dsr_shadow shadow +#define cd_port port +#define cd_shadow shadow + static const struct control_pins e100_modem_pins[NR_PORTS] = { /* Ser 0 */ { -#if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB) - R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow, - 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 +#ifdef CONFIG_ETRAX_SERIAL_PORT0 + E100_STRUCT_PORT(0,DTR), E100_STRUCT_SHADOW(0,DTR), + E100_STRUCT_MASK(0,DTR), + E100_STRUCT_MASK(0,RI), + E100_STRUCT_MASK(0,DSR), + E100_STRUCT_MASK(0,CD) #else - &dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3 -#endif + CONTROL_PINS_PORT_NOT_USED(0) +#endif }, /* Ser 1 */ { -#if defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB) - R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow, - 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 +#ifdef CONFIG_ETRAX_SERIAL_PORT1 + E100_STRUCT_PORT(1,DTR), E100_STRUCT_SHADOW(1,DTR), + E100_STRUCT_MASK(1,DTR), + E100_STRUCT_MASK(1,RI), + E100_STRUCT_MASK(1,DSR), + E100_STRUCT_MASK(1,CD) #else - &dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3 -#endif + CONTROL_PINS_PORT_NOT_USED(1) +#endif }, /* Ser 2 */ { -#if defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA) - R_PORT_PA_DATA, &port_pa_data_shadow, &port_pa_dir_shadow, - 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 +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + E100_STRUCT_PORT(2,DTR), E100_STRUCT_SHADOW(2,DTR), + E100_STRUCT_MASK(2,DTR), + E100_STRUCT_MASK(2,RI), + E100_STRUCT_MASK(2,DSR), + E100_STRUCT_MASK(2,CD) #else - &dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3 -#endif + CONTROL_PINS_PORT_NOT_USED(2) +#endif }, /* Ser 3 */ { - &dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3 +#ifdef CONFIG_ETRAX_SERIAL_PORT3 + E100_STRUCT_PORT(3,DTR), E100_STRUCT_SHADOW(3,DTR), + E100_STRUCT_MASK(3,DTR), + E100_STRUCT_MASK(3,RI), + E100_STRUCT_MASK(3,DSR), + E100_STRUCT_MASK(3,CD) +#else + CONTROL_PINS_PORT_NOT_USED(3) +#endif } }; +#endif /* !CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED */ #if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_RS485_ON_PA) unsigned char rs485_pa_port = CONFIG_ETRAX_RS485_ON_PA_BIT; @@ -623,20 +1104,16 @@ unsigned char rs485_pa_port = CONFIG_ETRAX_RS485_ON_PA_BIT; /* These are typically PA or PB and 0 means 0V, 1 means 3.3V */ /* Is an output */ -#define E100_DTR_GET(info) ((*e100_modem_pins[(info)->line].shadow) & (1 << e100_modem_pins[(info)->line].dtr_bit)) +#define E100_DTR_GET(info) ((*e100_modem_pins[(info)->line].dtr_shadow) & e100_modem_pins[(info)->line].dtr_mask) /* Normally inputs */ -#define E100_RI_GET(info) ((*e100_modem_pins[(info)->line].port) & (1 << e100_modem_pins[(info)->line].ri_bit)) -#define E100_CD_GET(info) ((*e100_modem_pins[(info)->line].port) & (1 << e100_modem_pins[(info)->line].cd_bit)) +#define E100_RI_GET(info) ((*e100_modem_pins[(info)->line].ri_port) & e100_modem_pins[(info)->line].ri_mask) +#define E100_CD_GET(info) ((*e100_modem_pins[(info)->line].cd_port) & e100_modem_pins[(info)->line].cd_mask) /* Input */ -#define E100_DSR_GET(info) ((*e100_modem_pins[(info)->line].port) & (1 << e100_modem_pins[(info)->line].dsr_bit)) +#define E100_DSR_GET(info) ((*e100_modem_pins[(info)->line].dsr_port) & e100_modem_pins[(info)->line].dsr_mask) -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - /* * tmp_buf is used as a temporary buffer by serial_write. We need to * lock it in case the memcpy_fromfs blocks while swapping in a page, @@ -653,43 +1130,6 @@ static DECLARE_MUTEX(tmp_buf_sem); static struct semaphore tmp_buf_sem = MUTEX; #endif -#ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST - -/* clock select 10 for timer 1 gives 230400 Hz */ -#define FASTTIMER_SELECT (10) -/* we use a source of 230400 Hz and a divider of 15 => 15360 Hz */ -#define FASTTIMER_DIV (15) - -/* fast flush timer stuff */ -static int fast_timer_started = 0; -static unsigned long int fast_timer_ints = 0; - -static void _INLINE_ start_flush_timer(void) -{ - if (fast_timer_started) - return; - - *R_TIMER_CTRL = r_timer_ctrl_shadow = - (r_timer_ctrl_shadow & - ~IO_MASK(R_TIMER_CTRL, timerdiv1) & - ~IO_MASK(R_TIMER_CTRL, tm1) & - ~IO_MASK(R_TIMER_CTRL, clksel1)) | - IO_FIELD(R_TIMER_CTRL, timerdiv1, FASTTIMER_DIV) | - IO_STATE(R_TIMER_CTRL, tm1, stop_ld) | - IO_FIELD(R_TIMER_CTRL, clksel1, FASTTIMER_SELECT); - - *R_TIMER_CTRL = r_timer_ctrl_shadow = - (r_timer_ctrl_shadow & ~IO_MASK(R_TIMER_CTRL, tm1)) | - IO_STATE(R_TIMER_CTRL, tm1, run); - - /* enable timer1 irq */ - - *R_IRQ_MASK0_SET = IO_STATE(R_IRQ_MASK0_SET, timer1, set); - - fast_timer_started = 1; -} -#endif /* CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST */ - /* Calculate the chartime depending on baudrate, numbor of bits etc. */ static void update_char_time(struct e100_serial * info) { @@ -754,7 +1194,7 @@ cflag_to_etrax_baud(unsigned int cflag) 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 */ } @@ -770,16 +1210,17 @@ cflag_to_etrax_baud(unsigned int cflag) * any general port. */ + static inline void e100_dtr(struct e100_serial *info, int set) { #ifndef CONFIG_SVINTO_SIM - unsigned char mask = (1 << e100_modem_pins[info->line].dtr_bit); + unsigned char mask = e100_modem_pins[info->line].dtr_mask; #ifdef SERIAL_DEBUG_IO printk("ser%i dtr %i mask: 0x%02X\n", info->line, set, mask); printk("ser%i shadow before 0x%02X get: %i\n", - info->line, *e100_modem_pins[info->line].shadow, + info->line, *e100_modem_pins[info->line].dtr_shadow, E100_DTR_GET(info)); #endif /* DTR is active low */ @@ -788,20 +1229,15 @@ e100_dtr(struct e100_serial *info, int set) save_flags(flags); cli(); - *e100_modem_pins[info->line].shadow &= ~mask; - *e100_modem_pins[info->line].shadow |= (set ? 0 : mask); - *e100_modem_pins[info->line].port = *e100_modem_pins[info->line].shadow; + *e100_modem_pins[info->line].dtr_shadow &= ~mask; + *e100_modem_pins[info->line].dtr_shadow |= (set ? 0 : mask); + *e100_modem_pins[info->line].dtr_port = *e100_modem_pins[info->line].dtr_shadow; restore_flags(flags); } -#if 0 - REG_SHADOW_SET(e100_modem_pins[info->line].port, - *e100_modem_pins[info->line].shadow, - e100_modem_pins[info->line].dtr_bit, !set); -#endif #ifdef SERIAL_DEBUG_IO printk("ser%i shadow after 0x%02X get: %i\n", - info->line, *e100_modem_pins[info->line].shadow, + info->line, *e100_modem_pins[info->line].dtr_shadow, E100_DTR_GET(info)); #endif #endif @@ -814,15 +1250,16 @@ static inline void e100_rts(struct e100_serial *info, int set) { #ifndef CONFIG_SVINTO_SIM -#ifdef SERIAL_DEBUG_IO - printk("ser%i rts %i\n", info->line, set); -#endif 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; +#ifdef SERIAL_DEBUG_IO + printk("ser%i rts %i\n", info->line, set); +#endif #endif } + /* If this behaves as a modem, RI and CD is an output */ static inline void e100_ri_out(struct e100_serial *info, int set) @@ -830,21 +1267,16 @@ e100_ri_out(struct e100_serial *info, int set) #ifndef CONFIG_SVINTO_SIM /* RI is active low */ { - unsigned char mask = (1 << e100_modem_pins[info->line].ri_bit); + unsigned char mask = e100_modem_pins[info->line].ri_mask; unsigned long flags; save_flags(flags); cli(); - *e100_modem_pins[info->line].shadow &= ~mask; - *e100_modem_pins[info->line].shadow |= (set ? 0 : mask); - *e100_modem_pins[info->line].port = *e100_modem_pins[info->line].shadow; + *e100_modem_pins[info->line].ri_shadow &= ~mask; + *e100_modem_pins[info->line].ri_shadow |= (set ? 0 : mask); + *e100_modem_pins[info->line].ri_port = *e100_modem_pins[info->line].ri_shadow; restore_flags(flags); } -#if 0 - REG_SHADOW_SET(e100_modem_pins[info->line].port, - *e100_modem_pins[info->line].shadow, - e100_modem_pins[info->line].ri_bit, !set); -#endif #endif } static inline void @@ -853,21 +1285,16 @@ e100_cd_out(struct e100_serial *info, int set) #ifndef CONFIG_SVINTO_SIM /* CD is active low */ { - unsigned char mask = (1 << e100_modem_pins[info->line].cd_bit); + unsigned char mask = e100_modem_pins[info->line].cd_mask; unsigned long flags; save_flags(flags); cli(); - *e100_modem_pins[info->line].shadow &= ~mask; - *e100_modem_pins[info->line].shadow |= (set ? 0 : mask); - *e100_modem_pins[info->line].port = *e100_modem_pins[info->line].shadow; + *e100_modem_pins[info->line].cd_shadow &= ~mask; + *e100_modem_pins[info->line].cd_shadow |= (set ? 0 : mask); + *e100_modem_pins[info->line].cd_port = *e100_modem_pins[info->line].cd_shadow; restore_flags(flags); } -#if 0 - REG_SHADOW_SET(e100_modem_pins[info->line].port, - *e100_modem_pins[info->line].shadow, - e100_modem_pins[info->line].cd_bit, !set); -#endif #endif } @@ -931,6 +1358,60 @@ e100_enable_txdma_irq(struct e100_serial *info) *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(); + if (info->line == 0) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused); + } else if (info->line == 1) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb); + } else if (info->line == 2) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0); + } else if (info->line == 3) { + 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(); + /* 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); +} + + #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. */ @@ -972,88 +1453,98 @@ e100_enable_rs485(struct tty_struct *tty,struct rs485_control *r) info->rs485.rts_after_sent = 0x01 & r->rts_after_sent; 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, + info->rs485.rts_after_sent, + info->rs485.enabled + ); +*/ return 0; } static int -e100_write_rs485(struct tty_struct *tty,struct rs485_write *r) +e100_write_rs485(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) { - int total; struct e100_serial * info = (struct e100_serial *)tty->driver_data; + int old_enabled = info->rs485.enabled; - /* If we are in RS-485 mode, we need to toggle RTS and disable - * the receiver before initiating a DMA transfer + /* rs485 is always implicitly enabled if we're using the ioctl() + * but it doesn't have to be set in the rs485_control + * (to be backward compatible with old apps) + * So we store, set and restore it. */ - e100_rts(info, info->rs485.rts_on_send); -#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) - e100_disable_rx(info); - e100_disable_rxdma_irq(info); -#endif - - if (info->rs485.delay_rts_before_send > 0) { - current->timeout = jiffies + (info->rs485.delay_rts_before_send * HZ)/1000; - current->state = TASK_INTERRUPTIBLE; - schedule(); - current->timeout = 0; - } - total = rs_write(tty, 1, (*r).outc, (*r).outc_size); - - /* If we are in RS-485 mode the following things has to be done: - * wait until DMA is ready - * wait on transmit shift register - * wait to toggle RTS - * enable the receiver - */ + info->rs485.enabled = 1; + /* rs_write now deals with RS485 if enabled */ + count = rs_write(tty, from_user, buf, count); + info->rs485.enabled = old_enabled; + return count; +} - /* Sleep until all sent */ - tty_wait_until_sent(tty, 0); -#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER - /* Now sleep a little more so that shift register is empty */ - schedule_usleep(info->char_time_usec * 2); -#else - { - unsigned int val; - /* wait on transmit shift register */ - do{ - get_lsr_info(info, &val); - }while (!(val & TIOCSER_TEMT)); - } -#endif +#ifdef CONFIG_ETRAX_FAST_TIMER +/* Timer function to toggle RTS when using FAST_TIMER */ +static void rs485_toggle_rts_timer_function(unsigned long data) +{ + struct e100_serial *info = (struct e100_serial *)data; + fast_timers_rs485[info->line].function = NULL; e100_rts(info, info->rs485.rts_after_sent); - #if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) e100_enable_rx(info); e100_enable_rxdma_irq(info); #endif - - return total; } #endif +#endif /* CONFIG_ETRAX_RS485 */ /* * ------------------------------------------------------------ * rs_stop() and rs_start() * * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. + * They enable or disable transmitter using the XOFF registers, as necessary. * ------------------------------------------------------------ */ -/* FIXME - when are these used and what is the purpose ? - * In rs_stop we probably just can block the transmit DMA ready irq - * and in rs_start we re-enable it (and then the old one will come). - */ - static void rs_stop(struct tty_struct *tty) { + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + if (info) { + unsigned long flags; + unsigned long xoff; + + save_flags(flags); cli(); + 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 ) { + xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); + } + + *((unsigned long *)&info->port[REG_XOFF]) = xoff; + restore_flags(flags); + } } static void rs_start(struct tty_struct *tty) { + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + if (info) { + unsigned long flags; + unsigned long xoff; + + save_flags(flags); cli(); + 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 ) { + xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); + } + + *((unsigned long *)&info->port[REG_XOFF]) = xoff; + + restore_flags(flags); + } } /* @@ -1086,8 +1577,7 @@ rs_sched_event(struct e100_serial *info, int event) { info->event |= 1 << event; - queue_task(&info->tqueue, &tq_serial); - mark_bh(SERIAL_BH); + schedule_work(&info->work); } /* The output DMA channel is free - use it to send as many chars as possible @@ -1136,7 +1626,7 @@ transmit_chars(struct e100_serial *info) #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 with !tr_running\n"); return; } @@ -1173,26 +1663,16 @@ transmit_chars(struct e100_serial *info) /* our job here is done, don't schedule any new DMA transfer */ info->tr_running = 0; -#if defined(CONFIG_ETRAX_RS485) - /* Check if we should toggle RTS now */ +#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER) if (info->rs485.enabled) { - /* Make sure fifo is empty */ - int in_fifo = 0; - - do { - in_fifo = IO_EXTRACT(R_DMA_CH6_STATUS, avail, - *info->ostatusadr); - } while (in_fifo > 0); - /* Any way to really check transmitter empty? (TEMT) */ - /* Control RTS to set to RX mode */ - e100_rts(info, info->rs485.rts_after_sent); -#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) - e100_enable_rx(info); - e100_enable_rxdma_irq(info); -#endif + /* 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 */ - return; } @@ -1208,7 +1688,7 @@ transmit_chars(struct e100_serial *info) *info->ocmdadr = 1; /* dma command start -> R_DMAx_CMD */ /* DMA is now running (hopefully) */ -} +} /* transmit_chars */ static void start_transmit(struct e100_serial *info) @@ -1224,7 +1704,7 @@ start_transmit(struct e100_serial *info) info->tr_running = 1; transmit_chars(info); -} +} /* start_transmit */ #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER static int serial_fast_timer_started = 0; @@ -1254,49 +1734,91 @@ static void flush_timeout_function(unsigned long data); #define START_FLUSH_FAST_TIMER(info, string) #endif +static struct etrax_recv_buffer * +alloc_recv_buffer(unsigned int size) +{ + struct etrax_recv_buffer *buffer; + + if (!(buffer = kmalloc(sizeof *buffer + size, GFP_ATOMIC))) + return NULL; + + buffer->next = NULL; + buffer->length = 0; + buffer->error = TTY_NORMAL; + + return buffer; +} + +static void +append_recv_buffer(struct e100_serial *info, struct etrax_recv_buffer *buffer) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + if (!info->first_recv_buffer) + info->first_recv_buffer = buffer; + else + info->last_recv_buffer->next = buffer; + + info->last_recv_buffer = buffer; + + info->recv_cnt += buffer->length; + if (info->recv_cnt > info->max_recv_cnt) + info->max_recv_cnt = info->recv_cnt; + + restore_flags(flags); +} + static int add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag) { - if (!CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE)) + struct etrax_recv_buffer *buffer; + + if (!(buffer = alloc_recv_buffer(4))) return 0; - info->recv.buf[info->recv.head] = data; - info->flag_buf[info->recv.head] = flag; - info->recv.head = (info->recv.head + 1) & (SERIAL_RECV_SIZE - 1); + buffer->length = 1; + buffer->error = flag; + buffer->buffer[0] = data; + + append_recv_buffer(info, buffer); info->icount.rx++; return 1; } -static _INLINE_ unsigned int -copy_descr_data(struct e100_serial *info, unsigned int recvl, unsigned char *buf) +extern _INLINE_ unsigned int +handle_descr_data(struct e100_serial *info, struct etrax_dma_descr *descr, unsigned int recvl) { - unsigned int count = CIRC_SPACE_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE); - unsigned int length = 0; + struct etrax_recv_buffer *buffer = phys_to_virt(descr->buf) - sizeof *buffer; - while (length < recvl && count) { - if (length + count > recvl) - count = recvl - length; + if (info->recv_cnt + recvl > 65536) { + printk(KERN_CRIT + "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __FUNCTION__, recvl); + return 0; + } - memcpy(info->recv.buf + info->recv.head, buf + length, count); - memset(info->flag_buf + info->recv.head, '\0', count); - info->recv.head = (info->recv.head + count) & (SERIAL_RECV_SIZE - 1); - length += count; + buffer->length = recvl; - count = CIRC_SPACE_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE); - } + if (info->errorcode == ERRCODE_SET_BREAK) + buffer->error = TTY_BREAK; + info->errorcode = 0; - if (length != recvl) { - printk(__FUNCTION__ ": Buffer overflow! %d byte(s) did not fit.\n", recvl - length); - PROCSTAT(ser_stat[info->line].overrun_cnt += recvl - length); - } + append_recv_buffer(info, buffer); - return length; + if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE))) + panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__); + + descr->buf = virt_to_phys(buffer->buffer); + + return recvl; } static _INLINE_ unsigned int -copy_all_descr_data(struct e100_serial *info) +handle_all_descr_data(struct e100_serial *info) { struct etrax_dma_descr *descr; unsigned int recvl; @@ -1330,7 +1852,7 @@ copy_all_descr_data(struct e100_serial *info) /* update stats */ info->icount.rx += recvl; - ret += copy_descr_data(info, recvl, phys_to_virt(descr->buf)); + ret += handle_descr_data(info, descr, recvl); } return ret; @@ -1341,7 +1863,6 @@ receive_chars(struct e100_serial *info) { struct tty_struct *tty; unsigned char rstat; - unsigned int old_head; #ifdef CONFIG_SVINTO_SIM /* No receive in the simulator. Will probably be when the rest of @@ -1366,12 +1887,7 @@ receive_chars(struct e100_serial *info) if (info->errorcode == ERRCODE_INSERT_BREAK) add_char_and_flag(info, '\0', TTY_BREAK); - old_head = info->recv.head; - - if (copy_all_descr_data(info) && info->errorcode == ERRCODE_SET_BREAK) - info->flag_buf[old_head] = TTY_BREAK; - - info->errorcode = 0; + handle_all_descr_data(info); /* Read the status register to detect errors */ rstat = info->port[REG_STATUS]; @@ -1394,10 +1910,6 @@ receive_chars(struct e100_serial *info) add_char_and_flag(info, data, TTY_FRAME); } - if (!E100_RTS_GET(info) && - CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE) < TTY_THROTTLE_LIMIT) - info->tty->driver->throttle(info->tty); - START_FLUSH_FAST_TIMER(info, "receive_chars"); /* Restart the receiving DMA */ @@ -1408,19 +1920,20 @@ static _INLINE_ int start_recv_dma(struct e100_serial *info) { struct etrax_dma_descr *descr = info->rec_descr; - unsigned char *buf = info->recv.buf + 2*SERIAL_RECV_SIZE; + struct etrax_recv_buffer *buffer; int i; /* Set up the receiving descriptors */ for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) { + if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE))) + panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__); + descr[i].ctrl = d_int; - descr[i].buf = virt_to_phys(buf); + descr[i].buf = virt_to_phys(buffer->buffer); descr[i].sw_len = SERIAL_DESCR_BUF_SIZE; descr[i].hw_len = 0; descr[i].status = 0; descr[i].next = virt_to_phys(&descr[i+1]); - - buf += SERIAL_DESCR_BUF_SIZE; } /* Link the last descriptor to the first */ @@ -1448,7 +1961,7 @@ start_receive(struct e100_serial *info) #endif /* 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)); @@ -1456,10 +1969,6 @@ start_receive(struct e100_serial *info) info->tty->flip.count = 0; start_recv_dma(info); - -#ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST - start_flush_timer(); -#endif } @@ -1480,13 +1989,14 @@ status_handle(struct e100_serial *info, unsigned short status) DMA8(ser1) when they have finished a descriptor with the intr flag set. */ -static void +static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs) { struct e100_serial *info; unsigned long ireg; int i; - + int handled = 0; + #ifdef CONFIG_SVINTO_SIM /* No receive in the simulator. Will probably be when the rest of * the serial interface works, and this piece will just be removed. @@ -1495,7 +2005,7 @@ tr_interrupt(int irq, void *dev_id, struct pt_regs * regs) const char *s = "What? tr_interrupt in simulator??\n"; SIMCOUT(s,strlen(s)); } - return; + return IRQ_HANDLED; #endif /* find out the line that caused this irq and get it from rs_table */ @@ -1504,10 +2014,11 @@ tr_interrupt(int irq, void *dev_id, struct pt_regs * regs) for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; - if (!info->uses_dma) + if (!info->enabled || !info->uses_dma) continue; /* check for dma_descr (don't need to check for dma_eop in output dma for serial */ if (ireg & info->irq) { + handled = 1; /* we can send a new dma bunch. make it so. */ DEBUG_LOG(info->line, "tr_interrupt %i\n", i); /* Read jiffies_usec first, @@ -1522,16 +2033,18 @@ tr_interrupt(int irq, void *dev_id, struct pt_regs * regs) /* FIXME: here we should really check for a change in the status lines and if so call status_handle(info) */ } + return IRQ_RETVAL(handled); } /* dma input channel interrupt handler */ -static void +static irqreturn_t rec_interrupt(int irq, void *dev_id, struct pt_regs * regs) { struct e100_serial *info; unsigned long ireg; int i; + int handled = 0; #ifdef CONFIG_SVINTO_SIM /* No receive in the simulator. Will probably be when the rest of @@ -1550,10 +2063,11 @@ rec_interrupt(int irq, void *dev_id, struct pt_regs * regs) for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; - if (!info->uses_dma) + if (!info->enabled || !info->uses_dma) continue; /* check for both dma_eop and dma_descr for the input dma channel */ if (ireg & ((info->irq << 2) | (info->irq << 3))) { + handled = 1; /* we have received something */ receive_chars(info); } @@ -1561,6 +2075,7 @@ rec_interrupt(int irq, void *dev_id, struct pt_regs * regs) /* FIXME: here we should really check for a change in the status lines and if so call status_handle(info) */ } + return IRQ_RETVAL(handled); } static _INLINE_ int @@ -1611,48 +2126,56 @@ force_eop_if_needed(struct e100_serial *info) static _INLINE_ void flush_to_flip_buffer(struct e100_serial *info) { - struct tty_struct *tty = info->tty; - unsigned int count = CIRC_CNT_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE); + struct tty_struct *tty; + struct etrax_recv_buffer *buffer; unsigned int length; unsigned long flags; - if (!count) + if (!info->first_recv_buffer) return; save_flags(flags); cli(); + if (!(tty = info->tty)) { + restore_flags(flags); + return; + } + length = tty->flip.count; - - do { + + while ((buffer = info->first_recv_buffer) && length < TTY_FLIPBUF_SIZE) { + unsigned int count = buffer->length; + if (length + count > TTY_FLIPBUF_SIZE) count = TTY_FLIPBUF_SIZE - length; - memcpy(tty->flip.char_buf_ptr + length, info->recv.buf + info->recv.tail, count); - memcpy(tty->flip.flag_buf_ptr + length, info->flag_buf + info->recv.tail, count); - info->recv.tail = ((info->recv.tail + count) & (SERIAL_RECV_SIZE-1)); + memcpy(tty->flip.char_buf_ptr + length, buffer->buffer, count); + memset(tty->flip.flag_buf_ptr + length, TTY_NORMAL, count); + tty->flip.flag_buf_ptr[length] = buffer->error; + length += count; - - count = CIRC_CNT_TO_END(info->recv.head, - info->recv.tail, - SERIAL_RECV_SIZE); - } while (length < TTY_FLIPBUF_SIZE && count); + info->recv_cnt -= count; + + if (count == buffer->length) { + info->first_recv_buffer = buffer->next; + kfree(buffer); + } else { + buffer->length -= count; + memmove(buffer->buffer, buffer->buffer + count, buffer->length); + buffer->error = TTY_NORMAL; + } + } + + if (!info->first_recv_buffer) + info->last_recv_buffer = NULL; tty->flip.count = length; restore_flags(flags); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,66) /* this includes a check for low-latency */ tty_flip_buffer_push(tty); -#else - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); -#endif - - /* unthrottle if we have throttled */ - if (E100_RTS_GET(info) && - CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE) > TTY_THROTTLE_LIMIT) - tty->driver->unthrottle(info->tty); } static _INLINE_ void @@ -1662,7 +2185,7 @@ check_flush_timeout(struct e100_serial *info) flush_to_flip_buffer(info); - if (CIRC_CNT(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE)) + if (info->first_recv_buffer) START_FLUSH_FAST_TIMER(info, "flip"); } @@ -1678,43 +2201,11 @@ static void flush_timeout_function(unsigned long data) check_flush_timeout(info); } -#elif defined(CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST) - -static void -timeout_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct e100_serial *info; - int i; - -#ifdef CONFIG_SVINTO_SIM - /* No receive in the simulator. Will probably be when the rest of - * the serial interface works, and this piece will just be removed. - */ - { - const char *s = "What? timeout_interrupt in simulator??\n"; - SIMCOUT(s,strlen(s)); - } - return; -#endif - - /* acknowledge the timer1 irq */ - *R_TIMER_CTRL = r_timer_ctrl_shadow | IO_STATE(R_TIMER_CTRL, i1, clr); - PROCSTAT(fast_timer_ints++); - - for (i = 0; i < NR_PORTS; i++) { - info = rs_table + i; - if (info->uses_dma) - check_flush_timeout(info); - } -} /* timeout_interrupt */ - #else /* dma fifo/buffer timeout handler forces an end-of-packet for the dma input channel if no chars have been received for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS/100 s. - If CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST is configured then this - handler is instead run at 15360 Hz. */ static struct timer_list flush_timer; @@ -1810,7 +2301,7 @@ TODO: The break will be delayed until an F or V character is received. */ -static void _INLINE_ handle_ser_interrupt(struct e100_serial *info) +extern irqreturn_t _INLINE_ handle_ser_interrupt(struct e100_serial *info) { unsigned char rstat = info->port[REG_STATUS]; @@ -1869,7 +2360,7 @@ static void _INLINE_ handle_ser_interrupt(struct e100_serial *info) } info->break_detected_cnt = 0; DEBUG_LOG(info->line, "#iERR s d %04X\n", - ((rstat & SER_ERROR_MASK) << 8) | data); + ((rstat & SER_ERROR_MASK) << 8) | data); } PROCSTAT(ser_stat[info->line].early_errors_cnt++); } else { /* It was a valid byte, now let the DMA do the rest */ @@ -1883,8 +2374,8 @@ static void _INLINE_ handle_ser_interrupt(struct e100_serial *info) * previous interrupt we should discard it. */ long elapsed_usec = - (curr_time - info->last_rx_active) * (1000000/HZ) + - curr_time_u - info->last_rx_active_usec; + (curr_time - info->last_rx_active) * (1000000/HZ) + + curr_time_u - info->last_rx_active_usec; if (elapsed_usec < 2*info->char_time_usec) { DEBUG_LOG(info->line, "FBRK %i\n", info->line); /* Report as BREAK (error) and let @@ -1911,25 +2402,29 @@ static void _INLINE_ handle_ser_interrupt(struct e100_serial *info) /* Restarting the DMA never hurts */ *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); START_FLUSH_FAST_TIMER(info, "ser_int"); + return IRQ_HANDLED; } /* handle_ser_interrupt */ -static void +static irqreturn_t ser_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct e100_serial *info; int i; + int handled = 0; for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; - if (!info->uses_dma) + if (!info->enabled || !info->uses_dma) continue; /* Which line caused the irq? */ if (*R_IRQ_MASK1_RD & (1U << (8+2*info->line))) { + handled = 1; handle_ser_interrupt(info); } } + return IRQ_RETVAL(handled); } /* ser_interrupt */ #endif @@ -1949,12 +2444,6 @@ ser_interrupt(int irq, void *dev_id, struct pt_regs *regs) * them using rs_sched_event(), and they get done here. */ static void -do_serial_bh(void) -{ - run_task_queue(&tq_serial); -} - -static void do_softint(void *private_) { struct e100_serial *info = (struct e100_serial *) private_; @@ -1972,53 +2461,25 @@ do_softint(void *private_) } } -/* - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_serial_hangup() -> tty->hangup() -> rs_hangup() - * - */ -static void -do_serial_hangup(void *private_) -{ - struct e100_serial *info = (struct e100_serial *) private_; - struct tty_struct *tty; - - tty = info->tty; - if (!tty) - return; - - tty_hangup(tty); -} - static int startup(struct e100_serial * info) { unsigned long flags; unsigned long xmit_page; - unsigned char *recv_page; + int i; xmit_page = get_zeroed_page(GFP_KERNEL); if (!xmit_page) return -ENOMEM; - recv_page = kmalloc(2 * SERIAL_RECV_SIZE + SERIAL_RECV_DESCRIPTORS * SERIAL_DESCR_BUF_SIZE, GFP_KERNEL); - if (!recv_page) { - free_page(xmit_page); - return -ENOMEM; - } - - save_flags(flags); cli(); + save_flags(flags); + cli(); /* if it was already initialized, skip this */ if (info->flags & ASYNC_INITIALIZED) { restore_flags(flags); free_page(xmit_page); - kfree(recv_page); return 0; } @@ -2027,13 +2488,6 @@ startup(struct e100_serial * info) else info->xmit.buf = (unsigned char *) xmit_page; - if (info->recv.buf) - kfree(recv_page); - else { - info->recv.buf = (unsigned char *) recv_page; - info->flag_buf = info->recv.buf + SERIAL_RECV_SIZE; - } - #ifdef SERIAL_DEBUG_OPEN printk("starting up ttyS%d (xmit_buf 0x%p, recv_buf 0x%p)...\n", info->line, info->xmit.buf, info->recv.buf); #endif @@ -2044,8 +2498,13 @@ startup(struct e100_serial * info) right? */ if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit.head = info->xmit.tail = 0; - info->recv.head = info->recv.tail = 0; + info->first_recv_buffer = info->last_recv_buffer = NULL; + info->recv_cnt = info->max_recv_cnt = 0; + + for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) + info->rec_descr[i].buf = NULL; /* No real action in the simulator, but may set info important to ioctl. */ @@ -2084,8 +2543,12 @@ startup(struct e100_serial * info) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit.head = info->xmit.tail = 0; - info->recv.head = info->recv.tail = 0; + info->first_recv_buffer = info->last_recv_buffer = NULL; + info->recv_cnt = info->max_recv_cnt = 0; + for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) + info->rec_descr[i].buf = 0; + /* * and set the speed and other flags of the serial port * this will start the rx/tx as well @@ -2137,6 +2600,9 @@ static void shutdown(struct e100_serial * info) { unsigned long flags; + struct etrax_dma_descr *descr = info->rec_descr; + struct etrax_recv_buffer *buffer; + int i; #ifndef CONFIG_SVINTO_SIM /* shut down the transmitter and receiver */ @@ -2173,11 +2639,12 @@ shutdown(struct e100_serial * info) info->xmit.buf = NULL; } - if (info->recv.buf) { - kfree(info->recv.buf); - info->recv.buf = NULL; - info->flag_buf = NULL; - } + for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) + if (descr[i].buf) { + buffer = phys_to_virt(descr[i].buf) - sizeof *buffer; + kfree(buffer); + descr[i].buf = 0; + } if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { /* hang up DTR and RTS if HUPCL is enabled */ @@ -2199,6 +2666,7 @@ static void change_speed(struct e100_serial *info) { unsigned int cflag; + unsigned long xoff; /* first some safety checks */ @@ -2248,10 +2716,21 @@ change_speed(struct e100_serial *info) 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) { @@ -2268,7 +2747,13 @@ change_speed(struct e100_serial *info) info->port[REG_TR_CTRL] = info->tx_ctrl; info->port[REG_REC_CTRL] = info->rx_ctrl; - *((unsigned long *)&info->port[REG_XOFF]) = 0; + 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 ) { + xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); + } + + *((unsigned long *)&info->port[REG_XOFF]) = xoff; #endif /* !CONFIG_SVINTO_SIM */ update_char_time(info); @@ -2301,9 +2786,9 @@ rs_flush_chars(struct tty_struct *tty) restore_flags(flags); } -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, + const unsigned char *buf, int count) { int c, ret = 0; struct e100_serial *info = (struct e100_serial *)tty->driver_data; @@ -2316,7 +2801,7 @@ rs_write(struct tty_struct * tty, int from_user, #ifdef SERIAL_DEBUG_DATA if (info->line == SERIAL_DEBUG_LINE) - printk("rs_write (%d), status %d\n", + printk("raw_write (%d), status %d\n", count, info->port[REG_STATUS]); #endif @@ -2402,7 +2887,74 @@ rs_write(struct tty_struct * tty, int from_user, } return ret; -} +} /* raw_write() */ + +static int +rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ +#if defined(CONFIG_ETRAX_RS485) + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + + if (info->rs485.enabled) + { + /* If we are in RS-485 mode, we need to toggle RTS and disable + * the receiver before initiating a DMA transfer + */ +#ifdef CONFIG_ETRAX_FAST_TIMER + /* Abort any started timer */ + fast_timers_rs485[info->line].function = NULL; + del_fast_timer(&fast_timers_rs485[info->line]); +#endif + e100_rts(info, info->rs485.rts_on_send); +#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) + e100_disable_rx(info); + e100_disable_rxdma_irq(info); +#endif + + if (info->rs485.delay_rts_before_send > 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((info->rs485.delay_rts_before_send * HZ)/1000); + } + } +#endif /* CONFIG_ETRAX_RS485 */ + + count = raw_write(tty, from_user, buf, count); + +#if defined(CONFIG_ETRAX_RS485) + if (info->rs485.enabled) + { + unsigned int val; + /* If we are in RS-485 mode the following has to be done: + * wait until DMA is ready + * wait on transmit shift register + * toggle RTS + * enable the receiver + */ + + /* Sleep until all sent */ + tty_wait_until_sent(tty, 0); +#ifdef CONFIG_ETRAX_FAST_TIMER + /* Now sleep a little more so that shift register is empty */ + schedule_usleep(info->char_time_usec * 2); +#endif + /* wait on transmit shift register */ + do{ + get_lsr_info(info, &val); + }while (!(val & TIOCSER_TEMT)); + + e100_rts(info, info->rs485.rts_after_sent); + +#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) + e100_enable_rx(info); + e100_enable_rxdma_irq(info); +#endif + } +#endif /* CONFIG_ETRAX_RS485 */ + + return count; +} /* rs_write */ + /* how much space is available in the xmit buffer? */ @@ -2451,20 +3003,23 @@ rs_flush_buffer(struct tty_struct *tty) * 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, + * just disable DMA channel 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; - printk("serial.c:rs_send_xchar not implemented!\n"); + e100_disable_txdma_channel(info); - info->x_char = ch; - if (ch) { - /* Make sure transmit interrupts are on */ - /* TODO. */ - } + /* Wait for tr_ready */ + while (!(info->port[REG_STATUS] & IO_MASK(R_SERIAL0_STATUS, tr_ready))) + /* wait */; + + /* Write the XON/XOFF char */ + info->port[REG_TR_DATA] = ch; + + e100_enable_txdma_channel(info); } /* @@ -2482,20 +3037,22 @@ rs_throttle(struct tty_struct * tty) 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)); #endif - + + /* Do RTS before XOFF since XOFF might take some time */ + if (tty->termios->c_cflag & CRTSCTS) { + /* Turn off RTS line (do this atomic) */ + save_flags(flags); + cli(); + e100_rts(info, 0); + restore_flags(flags); + } if (I_IXOFF(tty)) - info->x_char = 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); + rs_send_xchar(tty, STOP_CHAR(tty)); + } static void @@ -2505,24 +3062,27 @@ rs_unthrottle(struct tty_struct * tty) 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)); #endif - + + /* Do RTS before XOFF since XOFF might take some time */ + if (tty->termios->c_cflag & CRTSCTS) { + /* Assert RTS line (do this atomic) */ + save_flags(flags); + cli(); + e100_rts(info, 1); + restore_flags(flags); + } + 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); + } /* @@ -2594,9 +3154,7 @@ set_serial_info(struct e100_serial *info, info->type = new_serial.type; info->close_delay = new_serial.close_delay; info->closing_wait = new_serial.closing_wait; -#if (LINUX_VERSION_CODE > 0x20100) info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; -#endif check_and_exit: if (info->flags & ASYNC_INITIALIZED) { @@ -2641,8 +3199,8 @@ get_lsr_info(struct e100_serial * info, unsigned int *value) #ifdef SERIAL_DEBUG_IO struct state_str { - int state; - const char *str; + int state; + const char *str; }; const struct state_str control_state_str[] = { @@ -2690,14 +3248,15 @@ get_modem_info(struct e100_serial * info, unsigned int *value) E100_DSR_GET(info), E100_CTS_GET(info)); #endif + result = (!E100_RTS_GET(info) ? TIOCM_RTS : 0) | (!E100_DTR_GET(info) ? TIOCM_DTR : 0) - | (!E100_CD_GET(info) ? TIOCM_CAR : 0) | (!E100_RI_GET(info) ? TIOCM_RNG : 0) | (!E100_DSR_GET(info) ? TIOCM_DSR : 0) + | (!E100_CD_GET(info) ? TIOCM_CAR : 0) | (!E100_CTS_GET(info) ? TIOCM_CTS : 0); - + #ifdef SERIAL_DEBUG_IO printk("e100ser: modem state: %i 0x%08X\n", result, result); { @@ -2766,44 +3325,7 @@ set_modem_info(struct e100_serial * info, unsigned int cmd, return 0; } -/* - * This routine sends a break character out the serial port. - */ -#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ -static void -send_break(struct e100_serial * info, int duration) -{ - unsigned long flags; - - if (!info->port) - return; - - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + duration; - - save_flags(flags); - cli(); - - /* Go to manual mode and set the txd pin to 0 */ - - info->tx_ctrl &= 0x3F; /* Clear bit 7 (txd) and 6 (tr_enable) */ - info->port[REG_TR_CTRL] = info->tx_ctrl; - - /* wait for "duration" jiffies */ - - schedule(); - - 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 transceiver like this - * so we need to reset it - */ - *info->ocmdadr = 4; - restore_flags(flags); -} -#else static void rs_break(struct tty_struct *tty, int break_state) { @@ -2824,19 +3346,12 @@ rs_break(struct tty_struct *tty, int break_state) info->port[REG_TR_CTRL] = info->tx_ctrl; restore_flags(flags); } -#endif static int rs_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { struct e100_serial * info = (struct e100_serial *)tty->driver_data; -#if defined(CONFIG_ETRAX_RS485) || (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ - int error; -#endif -#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ - int retval; -#endif if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && @@ -2846,45 +3361,6 @@ rs_ioctl(struct tty_struct *tty, struct file * file, } switch (cmd) { -#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - if (!arg) { - send_break(info, HZ/4); /* 1/4 second */ - if (signal_pending(current)) - return -EINTR; - } - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - send_break(info, arg ? arg*(HZ/10) : HZ/4); - if (signal_pending(current)) - return -EINTR; - return 0; - case TIOCGSOFTCAR: - error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long)); - if (error) - return error; - put_fs_long(C_CLOCAL(tty) ? 1 : 0, - (unsigned long *) arg); - return 0; - case TIOCSSOFTCAR: - arg = get_fs_long((unsigned long *) arg); - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; -#endif case TIOCMGET: return get_modem_info(info, (unsigned int *) arg); case TIOCMBIS: @@ -2908,22 +3384,22 @@ rs_ioctl(struct tty_struct *tty, struct file * file, #if defined(CONFIG_ETRAX_RS485) case TIOCSERSETRS485: - error = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct rs485_control)); - - if (error) - return error; - - return e100_enable_rs485(tty, (struct rs485_control *) arg); + { + struct rs485_control rs485ctrl; + if (copy_from_user(&rs485ctrl, (struct rs485_control*)arg, sizeof(rs485ctrl))) + return -EFAULT; + + return e100_enable_rs485(tty, &rs485ctrl); + } case TIOCSERWRRS485: - error = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct rs485_write)); - - if (error) - return error; - - return e100_write_rs485(tty, (struct rs485_write *) arg); + { + struct rs485_write rs485wr; + if (copy_from_user(&rs485wr, (struct rs485_write*)arg, sizeof(rs485wr))) + return -EFAULT; + + return e100_write_rs485(tty, 1, rs485wr.outc, rs485wr.outc_size); + } #endif default: @@ -2991,12 +3467,13 @@ rs_close(struct tty_struct *tty, struct file * filp) * 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; } @@ -3149,7 +3626,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, return -EAGAIN; #endif } - + /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. @@ -3162,7 +3639,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, if (tty->termios->c_cflag & CLOCAL) do_clocal = 1; - + /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in @@ -3187,7 +3664,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, while (1) { save_flags(flags); cli(); - /* assert RTS and DTR */ + /* assert RTS and DTR */ e100_rts(info, 1); e100_dtr(info, 1); restore_flags(flags); @@ -3204,7 +3681,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, #endif break; } - if (!(info->flags & ASYNC_CLOSING) && do_clocal) + if (!(info->flags & ASYNC_CLOSING) && do_clocal) /* && (do_clocal || DCD_IS_ASSERTED) */ break; if (signal_pending(current)) { @@ -3258,17 +3735,15 @@ rs_open(struct tty_struct *tty, struct file * filp) return -ENODEV; #ifdef SERIAL_DEBUG_OPEN - printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name, - info->count); + printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name, + info->count); #endif info->count++; tty->driver_data = info; info->tty = tty; -#if (LINUX_VERSION_CODE > 0x20100) info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; -#endif if (!tmp_buf) { page = get_zeroed_page(GFP_KERNEL); @@ -3312,7 +3787,7 @@ rs_open(struct tty_struct *tty, struct file * filp) #endif return retval; } - + #ifdef SERIAL_DEBUG_OPEN printk("rs_open ttyS%d successful...\n", info->line); #endif @@ -3323,7 +3798,7 @@ rs_open(struct tty_struct *tty, struct file * filp) * /proc fs routines.... */ -static 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; @@ -3338,17 +3813,17 @@ static inline int line_info(char *buf, struct e100_serial *info) stat_buf[0] = 0; stat_buf[1] = 0; - if (E100_RTS_GET(info)) + if (!E100_RTS_GET(info)) strcat(stat_buf, "|RTS"); - if (E100_CTS_GET(info)) + if (!E100_CTS_GET(info)) strcat(stat_buf, "|CTS"); - if (E100_DTR_GET(info)) + if (!E100_DTR_GET(info)) strcat(stat_buf, "|DTR"); - if (E100_DSR_GET(info)) + if (!E100_DSR_GET(info)) strcat(stat_buf, "|DSR"); - if (E100_CD_GET(info)) + if (!E100_CD_GET(info)) strcat(stat_buf, "|CD"); - if (E100_RI_GET(info)) + if (!E100_RI_GET(info)) strcat(stat_buf, "|RI"); ret += sprintf(buf+ret, " baud:%d", info->baud); @@ -3357,6 +3832,10 @@ static inline int line_info(char *buf, struct e100_serial *info) (unsigned long)info->icount.tx, (unsigned long)info->icount.rx); + ret += sprintf(buf+ret, " rx_pend:%lu/%lu", + (unsigned long)info->recv_cnt, + (unsigned long)info->max_recv_cnt); + if (info->icount.frame) ret += sprintf(buf+ret, " fe:%lu", (unsigned long)info->icount.frame); @@ -3413,12 +3892,13 @@ done: static void show_serial_version(void) { - printk("ETRAX 100LX serial-driver %s, (c) 2000 Axis Communications AB\r\n", - serial_version); + printk(KERN_INFO + "ETRAX 100LX serial-driver %s, (c) 2000-2003 Axis Communications AB\r\n", + &serial_version[11]); /* "$Revision: x.yy" */ } /* rs_init inits the driver at boot (using the module_init chain) */ - + static struct tty_operations rs_ops = { .open = rs_open, .close = rs_close, @@ -3428,8 +3908,8 @@ static struct tty_operations rs_ops = { .chars_in_buffer = rs_chars_in_buffer, .flush_buffer = rs_flush_buffer, .ioctl = rs_ioctl, - .throttle = rs_throttle, - .unthrottle = rs_unthrottle, + .throttle = rs_throttle, + .unthrottle = rs_unthrottle, .set_termios = rs_set_termios, .stop = rs_stop, .start = rs_start, @@ -3451,12 +3931,10 @@ rs_init(void) return -ENOMEM; show_serial_version(); - - 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) +#if !defined(CONFIG_ETRAX_SERIAL_FAST_TIMER) init_timer(&flush_timer); flush_timer.function = timed_flush_handler; mod_timer(&flush_timer, jiffies + CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS); @@ -3477,7 +3955,7 @@ rs_init(void) tty_set_operations(driver, &rs_ops); if (tty_register_driver(driver)) panic("Couldn't register serial driver\n"); - serial_driver = driver; + serial_driver = driver; /* do some initializing for the separate ports */ @@ -3495,15 +3973,12 @@ rs_init(void) info->event = 0; info->count = 0; info->blocked_open = 0; - info->tqueue.routine = do_softint; - info->tqueue.data = info; init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); info->xmit.buf = NULL; info->xmit.tail = info->xmit.head = 0; - info->recv.buf = NULL; - info->recv.tail = info->recv.head = 0; - info->flag_buf = NULL; + info->first_recv_buffer = info->last_recv_buffer = NULL; + info->recv_cnt = info->max_recv_cnt = 0; info->last_tx_active_usec = 0; info->last_tx_active = 0; @@ -3514,12 +3989,22 @@ rs_init(void) info->rs485.delay_rts_before_send = 0; info->rs485.enabled = 0; #endif + INIT_WORK(&info->work, do_softint, info); if (info->enabled) { printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n", serial_driver->name, info->line, (unsigned int)info->port); } } +#ifdef CONFIG_ETRAX_FAST_TIMER +#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER + memset(fast_timers, 0, sizeof(fast_timers)); +#endif +#ifdef CONFIG_ETRAX_RS485 + memset(fast_timers_rs485, 0, sizeof(fast_timers_rs485)); +#endif + fast_timer_init(); +#endif #ifndef CONFIG_SVINTO_SIM /* Not needed in simulator. May only complicate stuff. */ @@ -3531,7 +4016,7 @@ rs_init(void) panic("irq23"); #endif #ifdef SERIAL_HANDLE_EARLY_ERRORS - if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, SA_INTERRUPT, "serial ", NULL)) + if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial ", NULL)) panic("irq8"); #endif #ifdef CONFIG_ETRAX_SERIAL_PORT1 @@ -3555,12 +4040,6 @@ rs_init(void) panic("irq21"); #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"); - } -#endif #endif /* CONFIG_SVINTO_SIM */ return 0; diff --git a/arch/cris/drivers/serial.h b/arch/cris/arch-v10/drivers/serial.h index 1994228fa102..45813c657aaf 100644 --- a/arch/cris/drivers/serial.h +++ b/arch/cris/arch-v10/drivers/serial.h @@ -25,6 +25,15 @@ #define SERIAL_RECV_DESCRIPTORS 8 +struct etrax_recv_buffer { + struct etrax_recv_buffer *next; + unsigned short length; + unsigned char error; + unsigned char pad; + + unsigned char buffer[0]; +}; + struct e100_serial { int baud; volatile u8 *port; /* R_SERIALx_CTRL */ @@ -79,10 +88,12 @@ struct e100_serial { int count; /* # of fd on device */ int blocked_open; /* # of blocked opens */ struct circ_buf xmit; - struct circ_buf recv; - unsigned char *flag_buf; + struct etrax_recv_buffer *first_recv_buffer; + struct etrax_recv_buffer *last_recv_buffer; + unsigned int recv_cnt; + unsigned int max_recv_cnt; - struct tq_struct tqueue; + struct work_struct work; struct async_icount icount; /* error-statistics etc.*/ #ifdef DECLARE_WAITQUEUE wait_queue_head_t open_wait; @@ -101,7 +112,7 @@ struct e100_serial { int break_detected_cnt; int errorcode; -#ifdef CONFIG_RS485 +#ifdef CONFIG_ETRAX_RS485 struct rs485_control rs485; /* RS-485 support */ #endif }; diff --git a/arch/cris/arch-v10/kernel/Makefile b/arch/cris/arch-v10/kernel/Makefile new file mode 100644 index 000000000000..20a56080a8f0 --- /dev/null +++ b/arch/cris/arch-v10/kernel/Makefile @@ -0,0 +1,16 @@ +# $Id: Makefile,v 1.4 2003/07/04 12:57:13 tobiasa Exp $ +# +# Makefile for the linux kernel. +# + +extra-y := head.o + + +obj-y := entry.o traps.o shadows.o debugport.o irq.o \ + process.o setup.o signal.o traps.o time.o ptrace.o + +obj-$(CONFIG_ETRAX_KGDB) += kgdb.o +obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o + +clean: + diff --git a/arch/cris/arch-v10/kernel/asm-offsets.c b/arch/cris/arch-v10/kernel/asm-offsets.c new file mode 100644 index 000000000000..1aa3cc4e7107 --- /dev/null +++ b/arch/cris/arch-v10/kernel/asm-offsets.c @@ -0,0 +1,47 @@ +#include <linux/sched.h> +#include <asm/thread_info.h> + +/* + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed to extract + * and format the required data. + */ + +#define DEFINE(sym, val) \ + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + +#define BLANK() asm volatile("\n->" : : ) + +int main(void) +{ +#define ENTRY(entry) DEFINE(PT_ ## entry, offsetof(struct pt_regs, entry)) + ENTRY(orig_r10); + ENTRY(r13); + ENTRY(r12); + ENTRY(r11); + ENTRY(r10); + ENTRY(r9); + ENTRY(mof); + ENTRY(dccr); + ENTRY(srp); + BLANK(); +#undef ENTRY +#define ENTRY(entry) DEFINE(TI_ ## entry, offsetof(struct thread_info, entry)) + ENTRY(task); + ENTRY(flags); + ENTRY(preempt_count); + BLANK(); +#undef ENTRY +#define ENTRY(entry) DEFINE(THREAD_ ## entry, offsetof(struct thread_struct, entry)) + ENTRY(ksp); + ENTRY(usp); + ENTRY(dccr); + BLANK(); +#undef ENTRY +#define ENTRY(entry) DEFINE(TASK_ ## entry, offsetof(struct task_struct, entry)) + ENTRY(pid); + BLANK(); + DEFINE(LCLONE_VM, CLONE_VM); + DEFINE(LCLONE_UNTRACED, CLONE_UNTRACED); + return 0; +} diff --git a/arch/cris/kernel/debugport.c b/arch/cris/arch-v10/kernel/debugport.c index fa20b2146adf..012c3028e5f6 100644 --- a/arch/cris/kernel/debugport.c +++ b/arch/cris/arch-v10/kernel/debugport.c @@ -12,6 +12,33 @@ * init_etrax_debug() * * $Log: debugport.c,v $ + * Revision 1.11 2003/07/07 09:53:36 starvik + * Revert all the 2.5.74 merge changes to make the console work again + * + * Revision 1.9 2003/02/17 17:07:23 starvik + * Solved the problem with corrupted debug output (from Linux 2.4) + * * Wait until DMA, FIFO and pipe is empty before and after transmissions + * * Buffer data until a FIFO flush can be triggered. + * + * Revision 1.8 2003/01/22 06:48:36 starvik + * Fixed warnings issued by GCC 3.2.1 + * + * Revision 1.7 2002/12/12 08:26:32 starvik + * Don't use C-comments inside CVS comments + * + * Revision 1.6 2002/12/11 15:42:02 starvik + * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/ + * + * Revision 1.5 2002/11/20 06:58:03 starvik + * Compiles with kgdb + * + * Revision 1.4 2002/11/19 14:35:24 starvik + * Changes from linux 2.4 + * Changed struct initializer syntax to the currently prefered notation + * + * Revision 1.3 2002/11/06 09:47:03 starvik + * Modified for new interrupt macros + * * Revision 1.2 2002/01/21 15:21:50 bjornw * Update for kdev_t changes * @@ -31,9 +58,10 @@ #include <linux/console.h> #include <linux/init.h> #include <linux/major.h> +#include <linux/delay.h> #include <asm/system.h> -#include <asm/svinto.h> +#include <asm/arch/svinto.h> #include <asm/io.h> /* Get SIMCOUT. */ /* Which serial-port is our debug port ? */ @@ -94,6 +122,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. @@ -102,9 +132,13 @@ static void 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 */ @@ -117,16 +151,48 @@ console_write(struct console *co, const char *buf, unsigned int len) return; #endif - save_flags(flags); - cli(); + local_save_flags(flags); + local_irq_disable(); #ifdef CONFIG_ETRAX_KGDB /* kgdb needs to output debug info using the gdb protocol */ putDebugString(buf, len); - restore_flags(flags); + local_irq_restore(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 */ + local_irq_restore(flags); + return; + } + } + /* make sure the transmitter is enabled. * NOTE: this overrides any setting done in ttySx, to 8N1, no auto-CTS. * in the future, move the tr/rec_ctrl shadows from etrax100ser.c to @@ -134,38 +200,37 @@ console_write(struct console *co, const char *buf, unsigned int len) */ *DEBUG_TR_CTRL = 0x40; - - /* 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 */ ; + 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 (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 */ - - restore_flags(flags); + tmp_size = 0; + local_irq_restore(flags); } /* legacy function */ @@ -227,11 +292,11 @@ console_setup(struct console *co, char *options) } static struct console sercons = { - .name = "ttyS", - .write = console_write, - .read = NULL, - .device = console_device, - .unblank = NULL, + .name = "ttyS", + .write = console_write, + .read = NULL, + .device = console_device, + .unblank = NULL, .setup = console_setup, .flags = CON_PRINTBUFFER, .index = DEBUG_PORT_IDX, diff --git a/arch/cris/kernel/entry.S b/arch/cris/arch-v10/kernel/entry.S index 2c0477bcdd50..5e5f5be7e139 100644 --- a/arch/cris/kernel/entry.S +++ b/arch/cris/arch-v10/kernel/entry.S @@ -1,12 +1,55 @@ -/* $Id: entry.S,v 1.3 2002/01/21 15:22:20 bjornw Exp $ +/* $Id: entry.S,v 1.16 2003/07/04 08:27:41 starvik Exp $ * * linux/arch/cris/entry.S * - * Copyright (C) 2000, 2001 Axis Communications AB + * Copyright (C) 2000, 2001, 2002 Axis Communications AB * * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: entry.S,v $ + * Revision 1.16 2003/07/04 08:27:41 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.15 2003/04/09 07:32:55 starvik + * resume should return task_struct, not thread_info + * + * Revision 1.14 2003/04/09 05:20:44 starvik + * Merge of Linux 2.5.67 + * + * Revision 1.13 2002/12/11 15:42:02 starvik + * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/*.c + * + * Revision 1.12 2002/12/10 09:00:10 starvik + * Merge of Linux 2.5.51 + * + * Revision 1.11 2002/12/05 07:53:10 starvik + * Corrected constants used with btstq + * + * Revision 1.10 2002/11/27 08:45:10 starvik + * pid is in task_struct, not thread_info + * + * Revision 1.9 2002/11/26 09:52:05 starvik + * Added preemptive kernel scheduling (if CONFIG_PREEMPT) + * + * Revision 1.8 2002/11/20 11:56:11 starvik + * Merge of Linux 2.5.48 + * + * Revision 1.7 2002/11/18 13:02:42 starvik + * Added fourth parameter to do_notify_resume + * Minor cleanup + * + * Revision 1.6 2002/11/11 10:37:50 starvik + * Use new asm-offset defines + * Modified for new location of current->work etc + * Removed SYMBOL_NAME from syscalls + * Added some new syscalls + * + * Revision 1.5 2002/11/05 06:45:11 starvik + * Merge of Linux 2.5.45 + * + * Revision 1.4 2002/02/05 15:41:31 bjornw + * Rewritten to conform better to current 2.5 code (similar to arch/i386) + * * Revision 1.3 2002/01/21 15:22:20 bjornw * NICE_DOGGY fix from 2.4 arch/cris * @@ -185,75 +228,88 @@ #include <linux/linkage.h> #include <linux/sys.h> #include <asm/unistd.h> -#include <asm/sv_addr_ag.h> +#include <asm/arch/sv_addr_ag.h> #include <asm/errno.h> - +#include <asm/thread_info.h> +#include <asm/arch/offset.h> + ;; functions exported from this file .globl system_call .globl ret_from_intr - .globl ret_from_sys_call + .globl ret_from_fork .globl resume .globl multiple_interrupt .globl hwbreakpoint .globl IRQ1_interrupt - .globl timer_interrupt - .globl timer_shortcut .globl spurious_interrupt .globl hw_bp_trigs .globl mmu_bus_fault .globl do_sigtrap .globl gdb_handle_breakpoint - .globl sys_call_table - ;; Get values and offsets into various structs. The file isn't - ;; suitable for consumption by the preprocessor, so don't use - ;; #include. - .include "entryoffsets.s" - - ;; process bits for ptrace. FIXME: Should be in a header file. - -PT_TRACESYS_BIT = 1 - ;; below are various parts of system_call which are not in the fast-path - ;; handle software irqs - -_handle_softirq: - move.d $r9, $r1 - jsr do_softirq ; call the C routine for softirq handling - move.d $r1, $r9 - - ;; fall-through - +#ifdef CONFIG_PREEMPT + ; Check if preemptive kernel scheduling should be done +_resume_kernel: + ; Load current task struct + movs.w -8192, $r0 ; THREAD_SIZE = 8192 + and.d $sp, $r0 + move.d [$r0+TI_preempt_count], $r10 ; Preemption disabled? + bne _Rexit + nop +_need_resched: + move.d [$r0+TI_flags], $r10 + btstq TIF_NEED_RESCHED, $r10 ; Check if need_resched is set + bpl _Rexit + nop + ; Ok, lets's do some preemptive kernel scheduling + move.d PREEMPT_ACTIVE, $r10 + move.d $r10, [$r0+TI_preempt_count] ; Mark as active + ei + jsr schedule + clear.d [$r0+TI_preempt_count] ; Mark as inactive + di + ; Load new task struct + movs.w -8192, $r0 ; THREAD_SIZE = 8192 + and.d $sp, $r0 + ; One more time (with new task) + ba _need_resched + nop +#else +#define _resume_kernel _Rexit +#endif + + ; Called at exit from fork. schedule_tail must be called to drop + ; spinlock if CONFIG_PREEMPT +ret_from_fork: + jsr schedule_tail + ba ret_from_sys_call + nop + ret_from_intr: - ;; check for resched only if we're going back to user-mode + ;; check for resched if preemptive kernel or if we're going back to user-mode ;; this test matches the user_regs(regs) macro ;; we cannot simply test $dccr, because that does not necessarily ;; reflect what mode we'll return into. - move.d [$sp + LDCCR], $r0; regs->dccr + move.d [$sp + PT_dccr], $r0; regs->dccr btstq 8, $r0 ; U-flag - bpl _Rexit ; go back directly - nop - ba _ret_with_reschedule ; go back but check schedule and signals first - nop + bpl _resume_kernel + ; Note that di below is in delay slot + +_resume_userspace: + di ; so need_resched and sigpending don't change -_reschedule: - ;; keep r9 intact - move.d $r9, $r1 - jsr schedule - ba ret_from_sys_call - move.d $r1, $r9 + movs.w -8192, $r0 ; THREAD_SIZE == 8192 + and.d $sp, $r0 - ;; return but call do_signal first -_signal_return: - ei ; we can get here from an interrupt - move.d $r9, $r10 ; do_signals syscall/irq param - moveq 0, $r11 ; oldset param - 0 in this case - move.d $sp, $r12 ; another argument to do_signal (the regs param) - jsr do_signal ; arch/cris/kernel/signal.c + move.d [$r0+TI_flags], $r10 ; current->work + and.d _TIF_WORK_MASK, $r10 ; is there any work to be done on return + bne _work_pending + nop ba _Rexit nop @@ -285,18 +341,20 @@ system_call: clear.d [$sp=$sp-4] ; frametype == 0, normal stackframe movs.w -ENOSYS, $r0 - move.d $r0, [$sp+LR10] ; put the default return value in r10 in the frame + move.d $r0, [$sp+PT_r10] ; put the default return value in r10 in the frame ;; check if this process is syscall-traced movs.w -8192, $r0 ; THREAD_SIZE == 8192 and.d $sp, $r0 - move.d [$r0+LTASK_PTRACE], $r0 - btstq PT_TRACESYS_BIT, $r0 - bmi _tracesys + move.d [$r0+TI_flags], $r0 + btstq TIF_SYSCALL_TRACE, $r0 + bmi _syscall_trace_entry nop +_syscall_traced: + ;; check for sanity in the requested syscall number cmpu.w NR_syscalls, $r9 @@ -317,39 +375,26 @@ system_call: jsr [$r9+sys_call_table] ; actually do the system call addq 3*4, $sp ; pop the mof, srp and regs parameters - move.d $r10, [$sp+LR10] ; save the return value + move.d $r10, [$sp+PT_r10] ; save the return value moveq 1, $r9 ; "parameter" to ret_from_sys_call to show it was a sys call ;; fall through into ret_from_sys_call to return ret_from_sys_call: - ;; r9 is a parameter - if 1, we came from a syscall, if 0, from an irq + ;; r9 is a parameter - if >=1 we came from a syscall, if 0, from an irq - ;; check if any bottom halves need service - - test.d [irq_stat] ; softirq_pending - bne _handle_softirq - nop + ;; get the current task-struct pointer (see top for defs) -_ret_with_reschedule: - ;; first get the current task-struct pointer (see top for defs) + movs.w -8192, $r0 ; THREAD_SIZE == 8192 + and.d $sp, $r0 - move.d $sp, $r0 - and.d -8192, $r0 ; THREAD_SIZE == 8192 - - ;; see if we want to reschedule into another process - - test.d [$r0+LTASK_NEEDRESCHED] - bne _reschedule + di ; make sure need_resched and sigpending don't change + move.d [$r0+TI_flags],$r1 + and.d _TIF_ALLWORK_MASK, $r1 + bne _syscall_exit_work nop - ;; see if we need to run signal checks (important that r9 is intact here) - - test.d [$r0+LTASK_SIGPENDING] - bne _signal_return - nop - _Rexit: ;; this epilogue MUST match the prologues in multiple_interrupt, irq.h and ptregs.h pop $r10 ; frametype @@ -375,63 +420,82 @@ _RBFexit: pop $srp ; subroutine return pointer rbf [$sp+] ; return by popping the CPU status -_tracesys: - ;; this first invocation of syscall_trace _requires_ that - ;; LR10 in the frame contains -ENOSYS (as is set in the beginning - ;; of system_call). - - jsr syscall_trace - - ;; now we should more or less do the same things as in the system_call - ;; but since our argument regs got clobbered during syscall_trace and - ;; because syscall_trace might want to alter them, we need to reload them - ;; from the stack-frame as we use them. - - ;; check for sanity in the requested syscall number - - move.d [$sp+LR9], $r9 - movs.w -ENOSYS, $r10 - cmpu.w NR_syscalls, $r9 - bcc 1f - lslq 2, $r9 ; multiply by 4, in the delay slot - - ;; read the system call vector entry into r9 + ;; We get here after doing a syscall if extra work might need to be done + ;; perform syscall exit tracing if needed - move.d [$r9+sys_call_table], $r9 - - ;; restore r10, r11, r12, r13, mof and srp into the needed registers - - move.d [$sp+LORIG_R10], $r10 ; LR10 is already filled with -ENOSYS. - move.d [$sp+LR11], $r11 - move.d [$sp+LR12], $r12 - move.d [$sp+LR13], $r13 - move [$sp+LMOF], $mof - move [$sp+LSRP], $srp - - ;; as a bonus 7th parameter, we give the location on the stack - ;; of the register structure itself. some syscalls need this. +_syscall_exit_work: + ;; $r0 contains current at this point and irq's are disabled - push $sp + move.d [$r0+TI_flags], $r1 + btstq TIF_SYSCALL_TRACE, $r1 + bpl _work_pending + nop - ;; the fifth and sixth parameters needs to be put on the stack for - ;; the system call to find them + ei - push $srp - push $mof + move.d $r9, $r1 ; preserve r9 + jsr do_syscall_trace + move.d $r1, $r9 + + ba _resume_userspace + nop + +_work_pending: + move.d [$r0+TI_flags], $r1 + btstq TIF_NEED_RESCHED, $r1 + bpl _work_notifysig ; was neither trace nor sched, must be signal/notify + nop + +_work_resched: + move.d $r9, $r1 ; preserve r9 + jsr schedule + move.d $r1, $r9 + di - jsr $r9 ; actually call the system-call - addq 3*4, $sp ; pop the srp, mof and regs parameters + move.d [$r0+TI_flags], $r1 + and.d _TIF_WORK_MASK, $r1; ignore the syscall trace counter + beq _Rexit + nop + btstq TIF_NEED_RESCHED, $r1 + bmi _work_resched ; current->work.need_resched + nop -1: move.d $r10, [$sp+LR10]; save the return value +_work_notifysig: + ;; deal with pending signals and notify-resume requests - ;; second call of syscall_trace, to let it grab the results - - jsr syscall_trace + move.d $r9, $r10 ; do_notify_resume syscall/irq param + moveq 0, $r11 ; oldset param - 0 in this case + move.d $sp, $r12 ; the regs param + move.d $r1, $r13 ; the thread_info_flags parameter + jsr do_notify_resume + + ba _Rexit + nop - moveq 1, $r9 ; "parameter" to ret_from_sys_call to show it was a sys call - ba ret_from_sys_call + ;; We get here as a sidetrack when we've entered a syscall with the + ;; trace-bit set. We need to call do_syscall_trace and then continue + ;; with the call. + +_syscall_trace_entry: + ;; PT_r10 in the frame contains -ENOSYS as required, at this point + + jsr do_syscall_trace + + ;; now re-enter the syscall code to do the syscall itself + ;; we need to restore $r9 here to contain the wanted syscall, and + ;; the other parameter-bearing registers + + move.d [$sp+PT_r9], $r9 + move.d [$sp+PT_orig_r10], $r10 ; PT_r10 is already filled with -ENOSYS. + move.d [$sp+PT_r11], $r11 + move.d [$sp+PT_r12], $r12 + move.d [$sp+PT_r13], $r13 + move [$sp+PT_mof], $mof + move [$sp+PT_srp], $srp + + ba _syscall_traced nop - + ;; resume performs the actual task-switching, by switching stack pointers ;; input arguments: r10 = prev, r11 = next, r12 = thread offset in task struct ;; returns old current in r10 @@ -442,26 +506,27 @@ _tracesys: resume: 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 + move $dccr, [$r10+THREAD_dccr]; save irq enable state di - move $usp, [$r10+LTHREAD_USP] ; save user-mode stackpointer + move $usp, [$r10+ THREAD_usp] ; save user-mode stackpointer ;; See copy_thread for the reason why register R9 is saved. subq 10*4, $sp movem $r9, [$sp] ; save non-scratch registers and R9. - move.d $sp, [$r10+LTHREAD_KSP] ; save the kernel stack pointer for the old task + move.d $sp, [$r10+THREAD_ksp] ; save the kernel stack pointer for the old task move.d $sp, $r10 ; return last running task in r10 - and.d -8192, $r10 ; get task ptr from stackpointer + and.d -8192, $r10 ; get thread_info from stackpointer + move.d [$r10+TI_task], $r10 ; get task add.d $r12, $r11 ; find the new tasks tss - move.d [$r11+LTHREAD_KSP], $sp ; switch into the new stackframe by restoring kernel sp + move.d [$r11+THREAD_ksp], $sp ; switch into the new stackframe by restoring kernel sp movem [$sp+], $r9 ; restore non-scratch registers and R9. - move [$r11+LTHREAD_USP], $usp ; restore user-mode stackpointer + move [$r11+THREAD_usp], $usp ; restore user-mode stackpointer - move [$r11+LTHREAD_DCCR], $dccr ; restore irq enable status + move [$r11+THREAD_dccr], $dccr ; restore irq enable status jump [$sp+] ; restore PC ;; This is the MMU bus fault handler. @@ -689,7 +754,8 @@ do_sigtrap: movs.w -8192,$r9 ; THREAD_SIZE == 8192 and.d $sp, $r9 - move.d [$r9+LTASK_PID], $r10 ; current->pid as arg1. + move.d [$r9+TI_task], $r10 + move.d [$r10+TASK_pid], $r10 ; current->pid as arg1. moveq 5, $r11 ; SIGTRAP as arg2. jsr sys_kill jump ret_from_intr ; Use the return routine for interrupts. @@ -722,75 +788,9 @@ hw_bp_trigs: hw_bp_trig_ptr: .dword hw_bp_trigs -/* - * This is the mechanism for creating a new kernel thread. - * - * NOTE! Only a kernel-only process (i.e. the swapper or direct descendants - * who haven't done an "execve()") should use this: it will work within - * a system call from a "real" process, but the process memory space will - * not be free'd until both the parent and the child have exited. - * - * This *can* be done in C with an single-asm-wrapped-in-a-function, but you - * get more or less gross code. The safer you make the asm-constraints, - * the grosser the code, at least with the gcc version in cris-dist-1.13. - */ - -/* int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) */ -/* r10 r11 r12 */ - - .text - .global kernel_thread -kernel_thread: - - /* Save ARG for later. */ - move.d $r11, $r13 - - /* r11 is argument 2 to clone, the flags */ - move.d $r12, $r11 - or.w LCLONE_VM, $r11 - or.w LCLONE_UNTRACED, $r11 - - /* Save FN for later. */ - move.d $r10, $r12 - - /* r9 contains syscall number, to sys_clone */ - movu.w __NR_clone, $r9 - - /* r10 is argument 1 to clone */ - clear.d $r10 - - /* call sys_clone, this will fork */ - break 13 - - /* parent or child? child returns 0 here. */ - test.d $r10 - - /* jump if parent */ - bne 1f - nop /* delay slot */ - - /* set argument to function to call */ - move.d $r13, $r10 - - /* call specified function */ - jsr $r12 - /* If we ever return from the function, something bad has happened. */ - - /* r9 is sys_exit syscall number */ - movu.w __NR_exit, $r9 - - /* Give a really bad exit-value */ - moveq -1, $r10 - - /* call sys_exit, killing the child */ - break 13 -1: - ret - nop /* delay slot */ - .section .rodata,"a" sys_call_table: - .long sys_ni_syscall /* 0 - old "setup()" system call*/ + .long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */ .long sys_exit .long sys_fork .long sys_read @@ -842,7 +842,7 @@ sys_call_table: .long sys_geteuid16 .long sys_getegid16 /* 50 */ .long sys_acct - .long sys_umount /* recycled never used phys() */ + .long sys_umount /* recycled never used phys( */ .long sys_ni_syscall /* old lock syscall holder */ .long sys_ioctl .long sys_fcntl /* 55 */ @@ -913,14 +913,14 @@ sys_call_table: .long sys_clone /* 120 */ .long sys_setdomainname .long sys_newuname - .long sys_ni_syscall /* TODO sys_modify_ldt - do something ?*/ + .long sys_ni_syscall /* sys_modify_ldt */ .long sys_adjtimex .long sys_mprotect /* 125 */ .long sys_sigprocmask - .long sys_create_module + .long sys_ni_syscall /* old "create_module" */ .long sys_init_module .long sys_delete_module - .long sys_get_kernel_syms /* 130 */ + .long sys_ni_syscall /* 130: old "get_kernel_syms" */ .long sys_quotactl .long sys_getpgid .long sys_fchdir @@ -945,19 +945,19 @@ sys_call_table: .long sys_mlockall .long sys_munlockall .long sys_sched_setparam - .long sys_sched_getparam /* 155 */ + .long sys_sched_getparam /* 155 */ .long sys_sched_setscheduler .long sys_sched_getscheduler .long sys_sched_yield .long sys_sched_get_priority_max - .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_get_priority_min /* 160 */ .long sys_sched_rr_get_interval .long sys_nanosleep .long sys_mremap .long sys_setresuid16 .long sys_getresuid16 /* 165 */ .long sys_ni_syscall /* sys_vm86 */ - .long sys_query_module + .long sys_ni_syscall /* Old sys_query_module */ .long sys_poll .long sys_nfsservctl .long sys_setresgid16 /* 170 */ @@ -970,8 +970,8 @@ sys_call_table: .long sys_rt_sigtimedwait .long sys_rt_sigqueueinfo .long sys_rt_sigsuspend - .long sys_pread /* 180 */ - .long sys_pwrite + .long sys_pread64 /* 180 */ + .long sys_pwrite64 .long sys_chown16 .long sys_getcwd .long sys_capget @@ -1013,11 +1013,54 @@ sys_call_table: .long sys_getdents64 /* 220 */ .long sys_fcntl64 .long sys_ni_syscall /* reserved for TUX */ - .long sys_ni_syscall /* Reserved for Security */ + .long sys_ni_syscall .long sys_gettid .long sys_readahead /* 225 */ + .long sys_setxattr + .long sys_lsetxattr + .long sys_fsetxattr + .long sys_getxattr + .long sys_lgetxattr /* 230 */ + .long sys_fgetxattr + .long sys_listxattr + .long sys_llistxattr + .long sys_flistxattr + .long sys_removexattr /* 235 */ + .long sys_lremovexattr + .long sys_fremovexattr .long sys_tkill - + .long sys_sendfile64 + .long sys_futex /* 240 */ + .long sys_sched_setaffinity + .long sys_sched_getaffinity + .long sys_ni_syscall /* sys_set_thread_area */ + .long sys_ni_syscall /* sys_get_thread_area */ + .long sys_io_setup /* 245 */ + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit + .long sys_io_cancel + .long sys_fadvise64 /* 250 */ + .long sys_ni_syscall + .long sys_exit_group + .long sys_lookup_dcookie + .long sys_epoll_create + .long sys_epoll_ctl /* 255 */ + .long sys_epoll_wait + .long sys_remap_file_pages + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 260 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 265 */ + .long sys_clock_getres + .long sys_clock_nanosleep + .long sys_statfs64 + .long sys_fstatfs64 + /* * NOTE!! This doesn't have to be exact - we just have * to make sure we have _enough_ of the "sys_ni_syscall" diff --git a/arch/cris/arch-v10/kernel/fasttimer.c b/arch/cris/arch-v10/kernel/fasttimer.c new file mode 100644 index 000000000000..2c2cb60c4710 --- /dev/null +++ b/arch/cris/arch-v10/kernel/fasttimer.c @@ -0,0 +1,996 @@ +/* $Id: fasttimer.c,v 1.4 2003/07/04 08:27:41 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.4 2003/07/04 08:27:41 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.3 2002/12/12 08:26:32 starvik + * Don't use C-comments inside CVS comments + * + * Revision 1.2 2002/12/11 15:42:02 starvik + * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/ + * + * Revision 1.1 2002/11/18 07:58:06 starvik + * Fast timers (from Linux 2.4) + * + * Revision 1.5 2002/10/15 06:21:39 starvik + * Added call to init_waitqueue_head + * + * Revision 1.4 2002/05/28 17:47:59 johana + * Added del_fast_timer() + * + * Revision 1.3 2002/05/28 16:16:07 johana + * Handle empty fast_timer_list + * + * Revision 1.2 2002/05/27 15:38:42 johana + * Made it compile without warnings on Linux 2.4. + * (includes, wait_queue, PROC_FS and snprintf) + * + * Revision 1.1 2002/05/27 15:32:25 johana + * arch/etrax100/kernel/fasttimer.c v1.8 from the elinux tree. + * + * Revision 1.8 2001/11/27 13:50:40 pkj + * Disable interrupts while stopping the timer and while modifying the + * list of active timers in timer1_handler() as it may be interrupted + * by other interrupts (e.g., the serial interrupt) which may add fast + * timers. + * + * Revision 1.7 2001/11/22 11:50:32 pkj + * * Only store information about the last 16 timers. + * * proc_fasttimer_read() now uses an allocated buffer, since it + * requires more space than just a page even for only writing the + * last 16 timers. The buffer is only allocated on request, so + * unless /proc/fasttimer is read, it is never allocated. + * * Renamed fast_timer_started to fast_timers_started to match + * fast_timers_added and fast_timers_expired. + * * Some clean-up. + * + * Revision 1.6 2000/12/13 14:02:08 johana + * Removed volatile for fast_timer_list + * + * Revision 1.5 2000/12/13 13:55:35 johana + * Added DEBUG_LOG, added som cli() and cleanup + * + * Revision 1.4 2000/12/05 13:48:50 johana + * Added range check when writing proc file, modified timer int handling + * + * Revision 1.3 2000/11/23 10:10:20 johana + * More debug/logging possibilities. + * Moved GET_JIFFIES_USEC() to timex.h and time.c + * + * Revision 1.2 2000/11/01 13:41:04 johana + * Clean up and bugfixes. + * Created new do_gettimeofday_fast() that gets a timeval struct + * with time based on jiffies and *R_TIMER0_DATA, uses a table + * for fast conversion of timer value to microseconds. + * (Much faster the standard do_gettimeofday() and we don't really + * wan't to use the true time - we wan't the "uptime" so timers don't screw up + * when we change the time. + * TODO: Add efficient support for continuous timers as well. + * + * Revision 1.1 2000/10/26 15:49:16 johana + * Added fasttimer, highresolution timers. + * + * Copyright (C) 2000,2001 2002 Axis Communications AB, Lund, Sweden + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/delay.h> + +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/delay.h> +#include <asm/rtc.h> + +#include <linux/config.h> +#include <linux/version.h> + +#include <asm/arch/svinto.h> +#include <asm/fasttimer.h> +#include <linux/proc_fs.h> + + +#define DEBUG_LOG_INCLUDED +#define FAST_TIMER_LOG +//#define FAST_TIMER_TEST + +#define FAST_TIMER_SANITY_CHECKS + +#ifdef FAST_TIMER_SANITY_CHECKS +#define SANITYCHECK(x) x +static int sanity_failed = 0; +#else +#define SANITYCHECK(x) +#endif + +#define D1(x) +#define D2(x) +#define DP(x) + +#define __INLINE__ inline + +static int fast_timer_running = 0; +static int fast_timers_added = 0; +static int fast_timers_started = 0; +static int fast_timers_expired = 0; +static int fast_timers_deleted = 0; +static int fast_timer_is_init = 0; +static int fast_timer_ints = 0; + +static struct fast_timer *fast_timer_list = NULL; + +#ifdef DEBUG_LOG_INCLUDED +#define DEBUG_LOG_MAX 128 +static const char * debug_log_string[DEBUG_LOG_MAX]; +static unsigned long debug_log_value[DEBUG_LOG_MAX]; +static int debug_log_cnt = 0; +static int debug_log_cnt_wrapped = 0; + +#define DEBUG_LOG(string, value) \ +{ \ + unsigned long log_flags; \ + save_flags(log_flags); \ + cli(); \ + debug_log_string[debug_log_cnt] = (string); \ + debug_log_value[debug_log_cnt] = (unsigned long)(value); \ + if (++debug_log_cnt >= DEBUG_LOG_MAX) \ + { \ + debug_log_cnt = debug_log_cnt % DEBUG_LOG_MAX; \ + debug_log_cnt_wrapped = 1; \ + } \ + restore_flags(log_flags); \ +} +#else +#define DEBUG_LOG(string, value) +#endif + + +/* The frequencies for index = clkselx number in R_TIMER_CTRL */ +#define NUM_TIMER_FREQ 15 +#define MAX_USABLE_TIMER_FREQ 7 +#define MAX_DELAY_US 853333L +const unsigned long timer_freq_100[NUM_TIMER_FREQ] = +{ + 3, /* 0 3333 - 853333 us */ + 6, /* 1 1666 - 426666 us */ + 12, /* 2 833 - 213333 us */ + 24, /* 3 416 - 106666 us */ + 48, /* 4 208 - 53333 us */ + 96, /* 5 104 - 26666 us */ + 192, /* 6 52 - 13333 us */ + 384, /* 7 26 - 6666 us */ + 576, + 1152, + 2304, + 4608, + 9216, + 18432, + 62500, + /* 15 = cascade */ +}; +#define NUM_TIMER_STATS 16 +#ifdef FAST_TIMER_LOG +struct fast_timer timer_added_log[NUM_TIMER_STATS]; +struct fast_timer timer_started_log[NUM_TIMER_STATS]; +struct fast_timer timer_expired_log[NUM_TIMER_STATS]; +#endif + +int timer_div_settings[NUM_TIMER_STATS]; +int timer_freq_settings[NUM_TIMER_STATS]; +int timer_delay_settings[NUM_TIMER_STATS]; + +/* Not true gettimeofday, only checks the jiffies (uptime) + useconds */ +void __INLINE__ do_gettimeofday_fast(struct timeval *tv) +{ + unsigned long sec = jiffies; + unsigned long usec = GET_JIFFIES_USEC(); + + usec += (sec % HZ) * (1000000 / HZ); + sec = sec / HZ; + + if (usec > 1000000) + { + usec -= 1000000; + sec++; + } + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +int __INLINE__ timeval_cmp(struct timeval *t0, struct timeval *t1) +{ + if (t0->tv_sec < t1->tv_sec) + { + return -1; + } + else if (t0->tv_sec > t1->tv_sec) + { + return 1; + } + if (t0->tv_usec < t1->tv_usec) + { + return -1; + } + else if (t0->tv_usec > t1->tv_usec) + { + return 1; + } + return 0; +} + +void __INLINE__ start_timer1(unsigned long delay_us) +{ + int freq_index = 0; /* This is the lowest resolution */ + unsigned long upper_limit = MAX_DELAY_US; + + unsigned long div; + /* Start/Restart the timer to the new shorter value */ + /* t = 1/freq = 1/19200 = 53us + * T=div*t, div = T/t = delay_us*freq/1000000 + */ +#if 1 /* Adaptive timer settings */ + while (delay_us < upper_limit && freq_index < MAX_USABLE_TIMER_FREQ) + { + freq_index++; + upper_limit >>= 1; /* Divide by 2 using shift */ + } + if (freq_index > 0) + { + freq_index--; + } +#else + freq_index = 6; +#endif + div = delay_us * timer_freq_100[freq_index]/10000; + if (div < 2) + { + /* Maybe increase timer freq? */ + div = 2; + } + if (div > 255) + { + div = 0; /* This means 256, the max the timer takes */ + /* If a longer timeout than the timer can handle is used, + * then we must restart it when it goes off. + */ + } + + timer_div_settings[fast_timers_started % NUM_TIMER_STATS] = div; + timer_freq_settings[fast_timers_started % NUM_TIMER_STATS] = freq_index; + timer_delay_settings[fast_timers_started % NUM_TIMER_STATS] = delay_us; + + D1(printk("start_timer1 : %d us freq: %i div: %i\n", + delay_us, freq_index, div)); + /* Clear timer1 irq */ + *R_IRQ_MASK0_CLR = IO_STATE(R_IRQ_MASK0_CLR, timer1, clr); + + /* Set timer values */ + *R_TIMER_CTRL = r_timer_ctrl_shadow = + (r_timer_ctrl_shadow & + ~IO_MASK(R_TIMER_CTRL, timerdiv1) & + ~IO_MASK(R_TIMER_CTRL, tm1) & + ~IO_MASK(R_TIMER_CTRL, clksel1)) | + IO_FIELD(R_TIMER_CTRL, timerdiv1, div) | + IO_STATE(R_TIMER_CTRL, tm1, stop_ld) | + IO_FIELD(R_TIMER_CTRL, clksel1, freq_index ); /* 6=c19k2Hz */ + + /* Ack interrupt */ + *R_TIMER_CTRL = r_timer_ctrl_shadow | + IO_STATE(R_TIMER_CTRL, i1, clr); + + /* Start timer */ + *R_TIMER_CTRL = r_timer_ctrl_shadow = + (r_timer_ctrl_shadow & ~IO_MASK(R_TIMER_CTRL, tm1)) | + IO_STATE(R_TIMER_CTRL, tm1, run); + + /* Enable timer1 irq */ + *R_IRQ_MASK0_SET = IO_STATE(R_IRQ_MASK0_SET, timer1, set); + fast_timers_started++; + fast_timer_running = 1; +} + +/* In version 1.4 this function takes 27 - 50 us */ +void start_one_shot_timer(struct fast_timer *t, + fast_timer_function_type *function, + unsigned long data, + unsigned long delay_us, + const char *name) +{ + unsigned long flags; + struct fast_timer *tmp; + + D1(printk("sft %s %d us\n", name, delay_us)); + + save_flags(flags); + cli(); + + do_gettimeofday_fast(&t->tv_set); + tmp = fast_timer_list; + + SANITYCHECK({ /* Check so this is not in the list already... */ + while (tmp != NULL) + { + if (tmp == t) + { + printk("timer name: %s data: 0x%08lX already in list!\n", name, data); + sanity_failed++; + return; + } + else + { + tmp = tmp->next; + } + } + tmp = fast_timer_list; + }); + + t->delay_us = delay_us; + t->function = function; + t->data = data; + t->name = name; + + t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000; + t->tv_expires.tv_sec = t->tv_set.tv_sec + delay_us / 1000000; + if (t->tv_expires.tv_usec > 1000000) + { + t->tv_expires.tv_usec -= 1000000; + t->tv_expires.tv_sec++; + } +#ifdef FAST_TIMER_LOG + timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t; +#endif + fast_timers_added++; + + /* Check if this should timeout before anything else */ + if (tmp == NULL || timeval_cmp(&t->tv_expires, &tmp->tv_expires) < 0) + { + /* Put first in list and modify the timer value */ + t->prev = NULL; + t->next = fast_timer_list; + if (fast_timer_list) + { + fast_timer_list->prev = t; + } + fast_timer_list = t; +#ifdef FAST_TIMER_LOG + timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t; +#endif + start_timer1(delay_us); + } else { + /* Put in correct place in list */ + while (tmp->next && + timeval_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0) + { + tmp = tmp->next; + } + /* Insert t after tmp */ + t->prev = tmp; + t->next = tmp->next; + if (tmp->next) + { + tmp->next->prev = t; + } + tmp->next = t; + } + + D2(printk("start_one_shot_timer: %d us done\n", delay_us)); + + restore_flags(flags); +} /* start_one_shot_timer */ + +static inline int fast_timer_pending (const struct fast_timer * t) +{ + return (t->next != NULL) || (t->prev != NULL) || (t == fast_timer_list); +} + +static inline int detach_fast_timer (struct fast_timer *t) +{ + struct fast_timer *next, *prev; + if (!fast_timer_pending(t)) + return 0; + next = t->next; + prev = t->prev; + if (next) + next->prev = prev; + if (prev) + prev->next = next; + else + fast_timer_list = next; + fast_timers_deleted++; + return 1; +} + +int del_fast_timer(struct fast_timer * t) +{ + unsigned long flags; + int ret; + + save_flags(flags); + cli(); + ret = detach_fast_timer(t); + t->next = t->prev = NULL; + restore_flags(flags); + return ret; +} /* del_fast_timer */ + + +/* Interrupt routines or functions called in interrupt context */ + +/* Timer 1 interrupt handler */ + +static irqreturn_t +timer1_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + struct fast_timer *t; + unsigned long flags; + + save_flags(flags); + cli(); + + /* Clear timer1 irq */ + *R_IRQ_MASK0_CLR = IO_STATE(R_IRQ_MASK0_CLR, timer1, clr); + + /* First stop timer, then ack interrupt */ + /* Stop timer */ + *R_TIMER_CTRL = r_timer_ctrl_shadow = + (r_timer_ctrl_shadow & ~IO_MASK(R_TIMER_CTRL, tm1)) | + IO_STATE(R_TIMER_CTRL, tm1, stop_ld); + + /* Ack interrupt */ + *R_TIMER_CTRL = r_timer_ctrl_shadow | IO_STATE(R_TIMER_CTRL, i1, clr); + + fast_timer_running = 0; + fast_timer_ints++; + + restore_flags(flags); + + t = fast_timer_list; + while (t) + { + struct timeval tv; + + /* Has it really expired? */ + do_gettimeofday_fast(&tv); + D1(printk("t: %is %06ius\n", tv.tv_sec, tv.tv_usec)); + + if (timeval_cmp(&t->tv_expires, &tv) <= 0) + { + /* Yes it has expired */ +#ifdef FAST_TIMER_LOG + timer_expired_log[fast_timers_expired % NUM_TIMER_STATS] = *t; +#endif + fast_timers_expired++; + + /* Remove this timer before call, since it may reuse the timer */ + save_flags(flags); + cli(); + if (t->prev) + { + t->prev->next = t->next; + } + else + { + fast_timer_list = t->next; + } + if (t->next) + { + t->next->prev = t->prev; + } + t->prev = NULL; + t->next = NULL; + restore_flags(flags); + + if (t->function != NULL) + { + t->function(t->data); + } + else + { + DEBUG_LOG("!timer1 %i function==NULL!\n", fast_timer_ints); + } + } + else + { + /* Timer is to early, let's set it again using the normal routines */ + D1(printk(".\n")); + } + + save_flags(flags); + cli(); + if ((t = fast_timer_list) != NULL) + { + /* Start next timer.. */ + long us; + struct timeval tv; + + do_gettimeofday_fast(&tv); + us = ((t->tv_expires.tv_sec - tv.tv_sec) * 1000000 + + t->tv_expires.tv_usec - tv.tv_usec); + if (us > 0) + { + if (!fast_timer_running) + { +#ifdef FAST_TIMER_LOG + timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t; +#endif + start_timer1(us); + } + restore_flags(flags); + break; + } + else + { + /* Timer already expired, let's handle it better late than never. + * The normal loop handles it + */ + D1(printk("e! %d\n", us)); + } + } + restore_flags(flags); + } + + if (!t) + { + D1(printk("t1 stop!\n")); + } + + return IRQ_HANDLED; +} + +static void wake_up_func(unsigned long data) +{ +#ifdef DECLARE_WAITQUEUE + wait_queue_head_t *sleep_wait_p = (wait_queue_head_t*)data; +#else + struct wait_queue **sleep_wait_p = (struct wait_queue **)data; +#endif + wake_up(sleep_wait_p); +} + + +/* Useful API */ + +void schedule_usleep(unsigned long us) +{ + struct fast_timer t; +#ifdef DECLARE_WAITQUEUE + wait_queue_head_t sleep_wait; + init_waitqueue_head(&sleep_wait); + { + DECLARE_WAITQUEUE(wait, current); +#else + struct wait_queue *sleep_wait = NULL; + struct wait_queue wait = { current, NULL }; +#endif + + D1(printk("schedule_usleep(%d)\n", us)); + add_wait_queue(&sleep_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us, + "usleep"); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&sleep_wait, &wait); + D1(printk("done schedule_usleep(%d)\n", us)); +#ifdef DECLARE_WAITQUEUE + } +#endif +} + +#ifdef CONFIG_PROC_FS +static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + ,int *eof, void *data_unused +#else + ,int unused +#endif + ); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +static struct proc_dir_entry *fasttimer_proc_entry; +#else +static struct proc_dir_entry fasttimer_proc_entry = +{ + 0, 9, "fasttimer", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, NULL /* ops -- default to array */, + &proc_fasttimer_read /* get_info */, +}; +#endif +#endif /* CONFIG_PROC_FS */ + +#ifdef CONFIG_PROC_FS + +/* This value is very much based on testing */ +#define BIG_BUF_SIZE (500 + NUM_TIMER_STATS * 300) + +static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + ,int *eof, void *data_unused +#else + ,int unused +#endif + ) +{ + unsigned long flags; + int i = 0; + int num_to_show; + struct timeval tv; + struct fast_timer *t, *nextt; + static char *bigbuf = NULL; + static unsigned long used; + + if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE))) + { + used = 0; + bigbuf[0] = '\0'; + return 0; + } + + if (!offset || !used) + { + do_gettimeofday_fast(&tv); + + used = 0; + used += sprintf(bigbuf + used, "Fast timers added: %i\n", + fast_timers_added); + used += sprintf(bigbuf + used, "Fast timers started: %i\n", + fast_timers_started); + used += sprintf(bigbuf + used, "Fast timer interrupts: %i\n", + fast_timer_ints); + used += sprintf(bigbuf + used, "Fast timers expired: %i\n", + fast_timers_expired); + used += sprintf(bigbuf + used, "Fast timers deleted: %i\n", + fast_timers_deleted); + used += sprintf(bigbuf + used, "Fast timer running: %s\n", + fast_timer_running ? "yes" : "no"); + used += sprintf(bigbuf + used, "Current time: %lu.%06lu\n", + (unsigned long)tv.tv_sec, + (unsigned long)tv.tv_usec); +#ifdef FAST_TIMER_SANITY_CHECKS + used += sprintf(bigbuf + used, "Sanity failed: %i\n", + sanity_failed); +#endif + used += sprintf(bigbuf + used, "\n"); + +#ifdef DEBUG_LOG_INCLUDED + { + int end_i = debug_log_cnt; + i = 0; + + if (debug_log_cnt_wrapped) + { + i = debug_log_cnt; + } + + while ((i != end_i || (debug_log_cnt_wrapped && !used)) && + used+100 < BIG_BUF_SIZE) + { + used += sprintf(bigbuf + used, debug_log_string[i], + debug_log_value[i]); + i = (i+1) % DEBUG_LOG_MAX; + } + } + used += sprintf(bigbuf + used, "\n"); +#endif + + num_to_show = (fast_timers_started < NUM_TIMER_STATS ? fast_timers_started: + NUM_TIMER_STATS); + used += sprintf(bigbuf + used, "Timers started: %i\n", fast_timers_started); + for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE) ; i++) + { + int cur = (fast_timers_started - i - 1) % NUM_TIMER_STATS; + +#if 1 //ndef FAST_TIMER_LOG + used += sprintf(bigbuf + used, "div: %i freq: %i delay: %i" + "\n", + timer_div_settings[cur], + timer_freq_settings[cur], + timer_delay_settings[cur] + ); +#endif +#ifdef FAST_TIMER_LOG + t = &timer_started_log[cur]; + used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " + "d: %6li us data: 0x%08lX" + "\n", + t->name, + (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_usec, + t->delay_us, + t->data + ); +#endif + } + used += sprintf(bigbuf + used, "\n"); + +#ifdef FAST_TIMER_LOG + num_to_show = (fast_timers_added < NUM_TIMER_STATS ? fast_timers_added: + NUM_TIMER_STATS); + used += sprintf(bigbuf + used, "Timers added: %i\n", fast_timers_added); + for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE); i++) + { + t = &timer_added_log[(fast_timers_added - i - 1) % NUM_TIMER_STATS]; + used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " + "d: %6li us data: 0x%08lX" + "\n", + t->name, + (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_usec, + t->delay_us, + t->data + ); + } + used += sprintf(bigbuf + used, "\n"); + + num_to_show = (fast_timers_expired < NUM_TIMER_STATS ? fast_timers_expired: + NUM_TIMER_STATS); + used += sprintf(bigbuf + used, "Timers expired: %i\n", fast_timers_expired); + for (i = 0; i < num_to_show && (used+100 < BIG_BUF_SIZE); i++) + { + t = &timer_expired_log[(fast_timers_expired - i - 1) % NUM_TIMER_STATS]; + used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " + "d: %6li us data: 0x%08lX" + "\n", + t->name, + (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_usec, + t->delay_us, + t->data + ); + } + used += sprintf(bigbuf + used, "\n"); +#endif + + used += sprintf(bigbuf + used, "Active timers:\n"); + save_flags(flags); + cli(); + t = fast_timer_list; + while (t != NULL && (used+100 < BIG_BUF_SIZE)) + { + nextt = t->next; + restore_flags(flags); + used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " + "d: %6li us data: 0x%08lX" +/* " func: 0x%08lX" */ + "\n", + t->name, + (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_usec, + t->delay_us, + t->data +/* , t->function */ + ); + cli(); + if (t->next != nextt) + { + printk("timer removed!\n"); + } + t = nextt; + } + restore_flags(flags); + } + + if (used - offset < len) + { + len = used - offset; + } + + memcpy(buf, bigbuf + offset, len); + *start = buf; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + *eof = 1; +#endif + + return len; +} +#endif /* PROC_FS */ + +#ifdef FAST_TIMER_TEST +static volatile unsigned long i = 0; +static volatile int num_test_timeout = 0; +static struct fast_timer tr[10]; +static int exp_num[10]; + +static struct timeval tv_exp[100]; + +static void test_timeout(unsigned long data) +{ + do_gettimeofday_fast(&tv_exp[data]); + exp_num[data] = num_test_timeout; + + num_test_timeout++; +} + +static void test_timeout1(unsigned long data) +{ + do_gettimeofday_fast(&tv_exp[data]); + exp_num[data] = num_test_timeout; + if (data < 7) + { + start_one_shot_timer(&tr[i], test_timeout1, i, 1000, "timeout1"); + i++; + } + num_test_timeout++; +} + +DP( +static char buf0[2000]; +static char buf1[2000]; +static char buf2[2000]; +static char buf3[2000]; +static char buf4[2000]; +); + +static char buf5[6000]; +static int j_u[1000]; + +static void fast_timer_test(void) +{ + int prev_num; + int j; + + struct timeval tv, tv0, tv1, tv2; + + printk("fast_timer_test() start\n"); + do_gettimeofday_fast(&tv); + + for (j = 0; j < 1000; j++) + { + j_u[j] = GET_JIFFIES_USEC(); + } + for (j = 0; j < 100; j++) + { + do_gettimeofday_fast(&tv_exp[j]); + } + printk("fast_timer_test() %is %06i\n", tv.tv_sec, tv.tv_usec); + + for (j = 0; j < 1000; j++) + { + printk("%i %i %i %i %i\n",j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]); + j += 4; + } + for (j = 0; j < 100; j++) + { + printk("%i.%i %i.%i %i.%i %i.%i %i.%i\n", + tv_exp[j].tv_sec,tv_exp[j].tv_usec, + tv_exp[j+1].tv_sec,tv_exp[j+1].tv_usec, + tv_exp[j+2].tv_sec,tv_exp[j+2].tv_usec, + tv_exp[j+3].tv_sec,tv_exp[j+3].tv_usec, + tv_exp[j+4].tv_sec,tv_exp[j+4].tv_usec); + j += 4; + } + do_gettimeofday_fast(&tv0); + start_one_shot_timer(&tr[i], test_timeout, i, 50000, "test0"); + DP(proc_fasttimer_read(buf0, NULL, 0, 0, 0)); + i++; + start_one_shot_timer(&tr[i], test_timeout, i, 70000, "test1"); + DP(proc_fasttimer_read(buf1, NULL, 0, 0, 0)); + i++; + start_one_shot_timer(&tr[i], test_timeout, i, 40000, "test2"); + DP(proc_fasttimer_read(buf2, NULL, 0, 0, 0)); + i++; + start_one_shot_timer(&tr[i], test_timeout, i, 60000, "test3"); + DP(proc_fasttimer_read(buf3, NULL, 0, 0, 0)); + i++; + start_one_shot_timer(&tr[i], test_timeout1, i, 55000, "test4xx"); + DP(proc_fasttimer_read(buf4, NULL, 0, 0, 0)); + i++; + do_gettimeofday_fast(&tv1); + + proc_fasttimer_read(buf5, NULL, 0, 0, 0); + + prev_num = num_test_timeout; + while (num_test_timeout < i) + { + if (num_test_timeout != prev_num) + { + prev_num = num_test_timeout; + } + } + do_gettimeofday_fast(&tv2); + printk("Timers started %is %06i\n", tv0.tv_sec, tv0.tv_usec); + printk("Timers started at %is %06i\n", tv1.tv_sec, tv1.tv_usec); + printk("Timers done %is %06i\n", tv2.tv_sec, tv2.tv_usec); + DP(printk("buf0:\n"); + printk(buf0); + printk("buf1:\n"); + printk(buf1); + printk("buf2:\n"); + printk(buf2); + printk("buf3:\n"); + printk(buf3); + printk("buf4:\n"); + printk(buf4); + ); + printk("buf5:\n"); + printk(buf5); + + printk("timers set:\n"); + for(j = 0; j<i; j++) + { + struct fast_timer *t = &tr[j]; + printk("%-10s set: %6is %06ius exp: %6is %06ius " + "data: 0x%08X func: 0x%08X\n", + t->name, + t->tv_set.tv_sec, + t->tv_set.tv_usec, + t->tv_expires.tv_sec, + t->tv_expires.tv_usec, + t->data, + t->function + ); + + printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n", + t->delay_us, + tv_exp[j].tv_sec, + tv_exp[j].tv_usec, + exp_num[j], + (tv_exp[j].tv_sec - t->tv_expires.tv_sec)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec); + } + proc_fasttimer_read(buf5, NULL, 0, 0, 0); + printk("buf5 after all done:\n"); + printk(buf5); + printk("fast_timer_test() done\n"); +} +#endif + + +void fast_timer_init(void) +{ + /* For some reason, request_irq() hangs when called froom time_init() */ + if (!fast_timer_is_init) + { +#if 0 && defined(FAST_TIMER_TEST) + int i; +#endif + + printk("fast_timer_init()\n"); + +#if 0 && defined(FAST_TIMER_TEST) + for (i = 0; i <= TIMER0_DIV; i++) + { + /* We must be careful not to get overflow... */ + printk("%3i %6u\n", i, timer0_value_us[i]); + } +#endif +#ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) + if ((fasttimer_proc_entry = create_proc_entry( "fasttimer", 0, 0 ))) + fasttimer_proc_entry->read_proc = proc_fasttimer_read; +#else + proc_register_dynamic(&proc_root, &fasttimer_proc_entry); +#endif +#endif /* PROC_FS */ + if(request_irq(TIMER1_IRQ_NBR, timer1_handler, SA_SHIRQ, + "fast timer int", NULL)) + { + printk("err: timer1 irq\n"); + } + fast_timer_is_init = 1; +#ifdef FAST_TIMER_TEST + printk("do test\n"); + fast_timer_test(); +#endif + } +} diff --git a/arch/cris/kernel/head.S b/arch/cris/arch-v10/kernel/head.S index bbd4eb3dd936..8192cd758b4f 100644 --- a/arch/cris/kernel/head.S +++ b/arch/cris/arch-v10/kernel/head.S @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.2 2001/12/18 13:35:19 bjornw Exp $ +/* $Id: head.S,v 1.6 2003/04/28 05:31:46 starvik Exp $ * * Head of the kernel - alter with care * @@ -7,6 +7,19 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: head.S,v $ + * Revision 1.6 2003/04/28 05:31:46 starvik + * Added section attributes + * + * Revision 1.5 2002/12/11 15:42:02 starvik + * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/*.c + * + * Revision 1.4 2002/11/07 09:00:44 starvik + * Names changed for init sections + * init_task_union -> init_thread_union + * + * Revision 1.3 2002/02/05 15:38:23 bjornw + * Oops.. non-CRAMFS_MAGIC should jump over the copying, not into it... + * * Revision 1.2 2001/12/18 13:35:19 bjornw * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). * @@ -161,7 +174,7 @@ #define ASSEMBLER_MACROS_ONLY /* The IO_* macros use the ## token concatenation operator, so -traditional must not be used when assembling this file. */ -#include <asm/sv_addr_ag.h> +#include <asm/arch/sv_addr_ag.h> #define CRAMFS_MAGIC 0x28cd3d45 #define RAM_INIT_MAGIC 0x56902387 @@ -294,7 +307,7 @@ ;; ;; arch/etrax100/etrax100.ld sets some symbols that define the start ;; and end of each segment. - + ;; Check if we start from DRAM or FLASH by testing PC move.d $pc,$r0 @@ -311,7 +324,7 @@ _inflash0: ;; Put this in a suitable section where we can reclaim storage ;; after init. - .section ".text.init" + .section ".init.text", "ax" _inflash: #ifdef CONFIG_ETRAX_ETHERNET ;; Start MII clock to make sure it is running when tranceiver is reset @@ -379,7 +392,7 @@ _dram_init_finished: _inram: ;; Move the ROM fs to after BSS end. This assumes that the cramfs ;; second longword contains the length of the cramfs - + moveq 0, $r0 move.d $r0, [romfs_length] ; default if there is no cramfs @@ -411,19 +424,20 @@ _inram: moveq 1, $r0 move.d $r0, [romfs_in_flash] - + jump _start_it ; enter code, cached this time _no_romfs_in_flash: + ;; Check if there is a cramfs (magic value). ;; Notice that we check for cramfs magic value - which is ;; the "rom fs" we'll possibly use in 2.4 if not JFFS (which does ;; not need this mechanism anyway) - + move.d __vmlinux_end, $r0; the image will be after the vmlinux end address move.d [$r0], $r1 ; cramfs assumes same endian on host/target cmp.d CRAMFS_MAGIC, $r1; magic value in cramfs superblock - bne 1f + bne 2f nop ;; Ok. What is its size ? @@ -453,7 +467,8 @@ _no_romfs_in_flash: subq 1, $r2 bne 1b nop - + +2: ;; Dont worry that the BSS is tainted. It will be cleared later. moveq 0, $r0 @@ -462,14 +477,15 @@ _no_romfs_in_flash: jump _start_it ; better skip the additional cramfs check below _start_it: + ;; the kernel stack is overlayed with the task structure for each ;; task. thus the initial kernel stack is in the same page as the ;; init_task (but starts in the top of the page, size 8192) - move.d init_task_union + 8192, $sp + move.d init_thread_union + 8192, $sp move.d ibr_start,$r0 ; this symbol is set by the linker script move $r0,$ibr move.d $r0,[etrax_irv] ; set the interrupt base register and pointer - + ;; Clear BSS region, from _bss_start to _end move.d __bss_start, $r0 @@ -839,5 +855,5 @@ swapper_pg_dir = 0x60002000 swapper_pg_dir = 0xc0002000 #endif - .section ".data.init" + .section ".init.data", "aw" #include "../lib/hw_settings.S" diff --git a/arch/cris/arch-v10/kernel/irq.c b/arch/cris/arch-v10/kernel/irq.c new file mode 100644 index 000000000000..c5e6348aba1f --- /dev/null +++ b/arch/cris/arch-v10/kernel/irq.c @@ -0,0 +1,219 @@ +/* $Id: irq.c,v 1.1 2002/12/11 15:42:02 starvik Exp $ + * + * linux/arch/cris/kernel/irq.c + * + * Copyright (c) 2000-2002 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + * This file contains the interrupt vectors and some + * helper functions + * + */ + +#include <asm/irq.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/config.h> + +irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */ + +/* don't use set_int_vector, it bypasses the linux interrupt handlers. it is + * global just so that the kernel gdb can use it. + */ + +void +set_int_vector(int n, irqvectptr addr, irqvectptr saddr) +{ + /* remember the shortcut entry point, after the prologue */ + + irq_shortcuts[n] = saddr; + + etrax_irv->v[n + 0x20] = (irqvectptr)addr; +} + +/* the breakpoint vector is obviously not made just like the normal irq handlers + * but needs to contain _code_ to jump to addr. + * + * the BREAK n instruction jumps to IBR + n * 8 + */ + +void +set_break_vector(int n, irqvectptr addr) +{ + unsigned short *jinstr = (unsigned short *)&etrax_irv->v[n*2]; + unsigned long *jaddr = (unsigned long *)(jinstr + 1); + + /* if you don't know what this does, do not touch it! */ + + *jinstr = 0x0d3f; + *jaddr = (unsigned long)addr; + + /* 00000026 <clrlop+1a> 3f0d82000000 jump 0x82 */ +} + +/* + * This builds up the IRQ handler stubs using some ugly macros in irq.h + * + * These macros create the low-level assembly IRQ routines that do all + * the operations that are needed. They are also written to be fast - and to + * disable interrupts as little as humanly possible. + * + */ + +/* IRQ0 and 1 are special traps */ +void hwbreakpoint(void); +void IRQ1_interrupt(void); +BUILD_TIMER_IRQ(2, 0x04) /* the timer interrupt is somewhat special */ +BUILD_IRQ(3, 0x08) +BUILD_IRQ(4, 0x10) +BUILD_IRQ(5, 0x20) +BUILD_IRQ(6, 0x40) +BUILD_IRQ(7, 0x80) +BUILD_IRQ(8, 0x100) +BUILD_IRQ(9, 0x200) +BUILD_IRQ(10, 0x400) +BUILD_IRQ(11, 0x800) +BUILD_IRQ(12, 0x1000) +BUILD_IRQ(13, 0x2000) +void mmu_bus_fault(void); /* IRQ 14 is the bus fault interrupt */ +void multiple_interrupt(void); /* IRQ 15 is the multiple IRQ interrupt */ +BUILD_IRQ(16, 0x10000) +BUILD_IRQ(17, 0x20000) +BUILD_IRQ(18, 0x40000) +BUILD_IRQ(19, 0x80000) +BUILD_IRQ(20, 0x100000) +BUILD_IRQ(21, 0x200000) +BUILD_IRQ(22, 0x400000) +BUILD_IRQ(23, 0x800000) +BUILD_IRQ(24, 0x1000000) +BUILD_IRQ(25, 0x2000000) +/* IRQ 26-30 are reserved */ +BUILD_IRQ(31, 0x80000000) + +/* + * Pointers to the low-level handlers + */ + +static void (*interrupt[NR_IRQS])(void) = { + NULL, NULL, IRQ2_interrupt, IRQ3_interrupt, + IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt, + IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt, + IRQ12_interrupt, IRQ13_interrupt, NULL, NULL, + IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt, + IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt, + IRQ24_interrupt, IRQ25_interrupt, NULL, NULL, NULL, NULL, NULL, + IRQ31_interrupt +}; + +static void (*sinterrupt[NR_IRQS])(void) = { + 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, + sIRQ24_interrupt, sIRQ25_interrupt, NULL, NULL, NULL, NULL, NULL, + sIRQ31_interrupt +}; + +static void (*bad_interrupt[NR_IRQS])(void) = { + NULL, NULL, + NULL, bad_IRQ3_interrupt, + bad_IRQ4_interrupt, bad_IRQ5_interrupt, + bad_IRQ6_interrupt, bad_IRQ7_interrupt, + bad_IRQ8_interrupt, bad_IRQ9_interrupt, + bad_IRQ10_interrupt, bad_IRQ11_interrupt, + bad_IRQ12_interrupt, bad_IRQ13_interrupt, + NULL, NULL, + bad_IRQ16_interrupt, bad_IRQ17_interrupt, + bad_IRQ18_interrupt, bad_IRQ19_interrupt, + bad_IRQ20_interrupt, bad_IRQ21_interrupt, + bad_IRQ22_interrupt, bad_IRQ23_interrupt, + bad_IRQ24_interrupt, bad_IRQ25_interrupt, + NULL, NULL, NULL, NULL, NULL, + bad_IRQ31_interrupt +}; + +void arch_setup_irq(int irq) +{ + set_int_vector(irq, interrupt[irq], sinterrupt[irq]); +} + +void arch_free_irq(int irq) +{ + set_int_vector(irq, bad_interrupt[irq], 0); +} + +void weird_irq(void); +void system_call(void); /* from entry.S */ +void do_sigtrap(void); /* from entry.S */ +void gdb_handle_breakpoint(void); /* from entry.S */ + +/* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and + setting the irq vector table to point to bad_interrupt ptrs. +*/ + +void __init +init_IRQ(void) +{ + int i; + + /* clear all interrupt masks */ + +#ifndef CONFIG_SVINTO_SIM + *R_IRQ_MASK0_CLR = 0xffffffff; + *R_IRQ_MASK1_CLR = 0xffffffff; + *R_IRQ_MASK2_CLR = 0xffffffff; +#endif + + *R_VECT_MASK_CLR = 0xffffffff; + + /* clear the shortcut entry points */ + + for(i = 0; i < NR_IRQS; i++) + irq_shortcuts[i] = NULL; + + for (i = 0; i < 256; i++) + etrax_irv->v[i] = weird_irq; + + /* the entries in the break vector contain actual code to be + executed by the associated break handler, rather than just a jump + address. therefore we need to setup a default breakpoint handler + for all breakpoints */ + + for (i = 0; i < 16; i++) + set_break_vector(i, do_sigtrap); + + /* set all etrax irq's to the bad handlers */ + for (i = 2; i < NR_IRQS; i++) + set_int_vector(i, bad_interrupt[i], 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); + set_int_vector(1, IRQ1_interrupt, 0); + + /* and irq 14 which is the mmu bus fault handler */ + + set_int_vector(14, mmu_bus_fault, 0); + + /* setup the system-call trap, which is reached by BREAK 13 */ + + set_break_vector(13, system_call); + + /* setup a breakpoint handler for debugging used for both user and + kernel mode debugging (which is why it is not inside an ifdef + CONFIG_ETRAX_KGDB) */ + set_break_vector(8, gdb_handle_breakpoint); + +#ifdef CONFIG_ETRAX_KGDB + /* setup kgdb if its enabled, and break into the debugger */ + kgdb_init(); + breakpoint(); +#endif +} diff --git a/arch/cris/kernel/kgdb.c b/arch/cris/arch-v10/kernel/kgdb.c index 793c77575c8e..4e5d50d28de5 100644 --- a/arch/cris/kernel/kgdb.c +++ b/arch/cris/arch-v10/kernel/kgdb.c @@ -18,8 +18,18 @@ *! Jul 21 1999 Bjorn Wesen eLinux port *! *! $Log: kgdb.c,v $ -*! Revision 1.1.1.1 2001/12/17 13:59:27 bjornw -*! Import of Linux 2.5.1 +*! Revision 1.4 2003/04/09 05:20:44 starvik +*! Merge of Linux 2.5.67 +*! +*! Revision 1.3 2003/01/21 19:11:08 starvik +*! Modified include path for new dir layout +*! +*! Revision 1.2 2002/11/19 14:35:24 starvik +*! Changes from linux 2.4 +*! Changed struct initializer syntax to the currently prefered notation +*! +*! Revision 1.1 2001/12/17 13:59:27 bjornw +*! Initial revision *! *! Revision 1.6 2001/10/09 13:10:03 matsfg *! Added $ on registers and removed some underscores @@ -58,7 +68,7 @@ *! *!--------------------------------------------------------------------------- *! -*! $Id: kgdb.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +*! $Id: kgdb.c,v 1.4 2003/04/09 05:20:44 starvik Exp $ *! *! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN *! @@ -216,7 +226,7 @@ #include <asm/setup.h> #include <asm/ptrace.h> -#include <asm/svinto.h> +#include <asm/arch/svinto.h> #include <asm/irq.h> static int kgdb_started = 0; @@ -1067,7 +1077,7 @@ handle_exception (int sigval) int regno = gdb_cris_strtol (&remcomInBuffer[1], &suffix, 16); int status; #ifdef PROCESS_SUPPORT - if (current_thread_g =! executing_task) + if (current_thread_g != executing_task) status = write_stack_register (current_thread_g, regno, suffix+1); else #endif diff --git a/arch/cris/arch-v10/kernel/process.c b/arch/cris/arch-v10/kernel/process.c new file mode 100644 index 000000000000..62e3a4fbf33a --- /dev/null +++ b/arch/cris/arch-v10/kernel/process.c @@ -0,0 +1,252 @@ +/* $Id: process.c,v 1.3 2003/07/04 08:27:41 starvik Exp $ + * + * linux/arch/cris/kernel/process.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (C) 2000-2002 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * Mikael Starvik (starvik@axis.com) + * + * This file handles the architecture-dependent parts of process handling.. + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/slab.h> + +#ifdef CONFIG_ETRAX_GPIO +void etrax_gpio_wake_up_check(void); /* drivers/gpio.c */ +#endif + +/* + * We use this if we don't have any better + * idle routine.. + */ +void default_idle(void) +{ +#ifdef CONFIG_ETRAX_GPIO + etrax_gpio_wake_up_check(); +#endif +} + +/* if the watchdog is enabled, we can simply disable interrupts and go + * into an eternal loop, and the watchdog will reset the CPU after 0.1s + * if on the other hand the watchdog wasn't enabled, we just enable it and wait + */ + +void hard_reset_now (void) +{ + /* + * Don't declare this variable elsewhere. We don't want any other + * code to know about it than the watchdog handler in entry.S and + * this code, implementing hard reset through the watchdog. + */ +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) + extern int cause_of_death; +#endif + + printk("*** HARD RESET ***\n"); + local_irq_disable(); + +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) + cause_of_death = 0xbedead; +#else + /* Since we dont plan to keep on reseting the watchdog, + the key can be arbitrary hence three */ + *R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, 3) | + IO_STATE(R_WATCHDOG, enable, start); +#endif + + while(1) /* waiting for RETRIBUTION! */ ; +} + +/* + * Return saved PC of a blocked thread. + */ +unsigned long thread_saved_pc(struct task_struct *t) +{ + return (unsigned long)user_regs(t->thread_info)->irp; +} + +static void kernel_thread_helper(void* dummy, int (*fn)(void *), void * arg) +{ + fn(arg); + do_exit(-1); /* Should never be called, return bad exit value */ +} + +/* + * Create a kernel thread + */ +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + + /* Don't use r10 since that is set to 0 in copy_thread */ + regs.r11 = (unsigned long)fn; + regs.r12 = (unsigned long)arg; + regs.irp = (unsigned long)kernel_thread_helper; + + /* Ok, create the new process.. */ + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); +} + +/* setup the child's kernel stack with a pt_regs and switch_stack on it. + * it will be un-nested during _resume and _ret_from_sys_call when the + * new thread is scheduled. + * + * also setup the thread switching structure which is used to keep + * thread-specific data during _resumes. + * + */ +asmlinkage void ret_from_fork(void); + +int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + unsigned long unused, + struct task_struct *p, struct pt_regs *regs) +{ + struct pt_regs * childregs; + struct switch_stack *swstack; + + /* put the pt_regs structure at the end of the new kernel stack page and fix it up + * remember that the task_struct doubles as the kernel stack for the task + */ + + childregs = user_regs(p->thread_info); + + *childregs = *regs; /* struct copy of pt_regs */ + + p->set_child_tid = p->clear_child_tid = NULL; + + childregs->r10 = 0; /* child returns 0 after a fork/clone */ + + /* put the switch stack right below the pt_regs */ + + swstack = ((struct switch_stack *)childregs) - 1; + + swstack->r9 = 0; /* parameter to ret_from_sys_call, 0 == dont restart the syscall */ + + /* we want to return into ret_from_sys_call after the _resume */ + + swstack->return_ip = (unsigned long) ret_from_fork; /* Will call ret_from_sys_call */ + + /* fix the user-mode stackpointer */ + + p->thread.usp = usp; + + /* and the kernel-mode one */ + + p->thread.ksp = (unsigned long) swstack; + +#ifdef DEBUG + printk("copy_thread: new regs at 0x%p, as shown below:\n", childregs); + show_registers(childregs); +#endif + + return 0; +} + +/* + * Be aware of the "magic" 7th argument in the four system-calls below. + * They need the latest stackframe, which is put as the 7th argument by + * entry.S. The previous arguments are dummies or actually used, but need + * to be defined to reach the 7th argument. + * + * N.B.: Another method to get the stackframe is to use current_regs(). But + * it returns the latest stack-frame stacked when going from _user mode_ and + * some of these (at least sys_clone) are called from kernel-mode sometimes + * (for example during kernel_thread, above) and thus cannot use it. Thus, + * to be sure not to get any surprises, we use the method for the other calls + * as well. + */ + +asmlinkage int sys_fork(long r10, long r11, long r12, long r13, long mof, long srp, + struct pt_regs *regs) +{ + return do_fork(SIGCHLD, rdusp(), regs, 0, NULL, NULL); +} + +/* if newusp is 0, we just grab the old usp */ +/* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */ +asmlinkage int sys_clone(unsigned long newusp, unsigned long flags, + int* parent_tid, int* child_tid, long mof, long srp, + struct pt_regs *regs) +{ + if (!newusp) + newusp = rdusp(); + return do_fork(flags & ~CLONE_IDLETASK, newusp, regs, 0, parent_tid, child_tid); +} + +/* vfork is a system call in i386 because of register-pressure - maybe + * we can remove it and handle it in libc but we put it here until then. + */ + +asmlinkage int sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp, + struct pt_regs *regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(const char *fname, char **argv, char **envp, + long r13, long mof, long srp, + struct pt_regs *regs) +{ + int error; + char *filename; + + filename = getname(fname); + error = PTR_ERR(filename); + + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, argv, envp, regs); + putname(filename); + out: + return error; +} + +/* + * These bracket the sleeping functions.. + */ + +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched ((unsigned long) scheduling_functions_start_here) +#define last_sched ((unsigned long) scheduling_functions_end_here) + +unsigned long get_wchan(struct task_struct *p) +{ +#if 0 + /* YURGH. TODO. */ + + unsigned long ebp, esp, eip; + unsigned long stack_page; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + stack_page = (unsigned long)p; + esp = p->thread.esp; + if (!stack_page || esp < stack_page || esp > 8188+stack_page) + return 0; + /* include/asm-i386/system.h:switch_to() pushes ebp last. */ + ebp = *(unsigned long *) esp; + do { + if (ebp < stack_page || ebp > 8184+stack_page) + return 0; + eip = *(unsigned long *) (ebp+4); + if (eip < first_sched || eip >= last_sched) + return eip; + ebp = *(unsigned long *) ebp; + } while (count++ < 16); +#endif + return 0; +} +#undef last_sched +#undef first_sched diff --git a/arch/cris/arch-v10/kernel/ptrace.c b/arch/cris/arch-v10/kernel/ptrace.c new file mode 100644 index 000000000000..c83eba5d6d04 --- /dev/null +++ b/arch/cris/arch-v10/kernel/ptrace.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2000-2003, Axis Communications AB. + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> + +#include <asm/uaccess.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/processor.h> + +/* + * Determines which bits in DCCR the user has access to. + * 1 = access, 0 = no access. + */ +#define DCCR_MASK 0x0000001f /* XNZVC */ + +extern inline long get_reg(struct task_struct *, unsigned int); +extern inline long put_reg(struct task_struct *, unsigned int, unsigned long); + +/* + * Called by kernel/ptrace.c when detaching. + * + * Make sure the single step bit is not set. + */ +void +ptrace_disable(struct task_struct *child) +{ + /* Todo - pending singlesteps? */ +} + +/* + * Note that this implementation of ptrace behaves differently from vanilla + * ptrace. Contrary to what the man page says, in the PTRACE_PEEKTEXT, + * PTRACE_PEEKDATA, and PTRACE_PEEKUSER requests the data variable is not + * ignored. Instead, the data variable is expected to point at a location + * (in user space) where the result of the ptrace call is written (instead of + * being returned). + */ +asmlinkage int +sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret; + + lock_kernel(); + ret = -EPERM; + + if (request == PTRACE_TRACEME) { + if (current->ptrace & PT_PTRACED) + goto out; + + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + + if (child) + get_task_struct(child); + + read_unlock(&tasklist_lock); + + if (!child) + goto out; + + ret = -EPERM; + + if (pid == 1) /* Leave the init process alone! */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + ret = ptrace_attach(child); + goto out_tsk; + } + + ret = -ESRCH; + + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + + if (child->parent != current) + goto out_tsk; + + switch (request) { + /* Read word at location address. */ + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + + if (copied != sizeof(tmp)) + break; + + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* Read the word at location address in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) + break; + + tmp = 0; /* Default return condition */ + ret = -EIO; + + if (addr < sizeof(struct pt_regs)) { + tmp = get_reg(child, addr >> 2); + ret = put_user(tmp, (unsigned long *)data); + } + + break; + } + + /* Write the word at location address. */ + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + ret = 0; + + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + break; + + ret = -EIO; + break; + + /* Write the word at location address in the USER area. */ + case PTRACE_POKEUSR: + ret = -EIO; + + if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) + break; + + if (addr < sizeof(struct pt_regs)) { + addr >>= 2; + + if (addr == PT_DCCR) { + /* + * Don't allow the tracing process to + * change stuff like interrupt enable, + * kernel/user bit, etc. + */ + data &= DCCR_MASK; + data |= get_reg(child, PT_DCCR) & ~DCCR_MASK; + } + + if (put_reg(child, addr, data)) + break; + + ret = 0; + } + break; + + case PTRACE_SYSCALL: + case PTRACE_CONT: + ret = -EIO; + + if ((unsigned long) data > _NSIG) + break; + + if (request == PTRACE_SYSCALL) { + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + else { + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + } + + child->exit_code = data; + + /* TODO: make sure any pending breakpoint is killed */ + wake_up_process(child); + ret = 0; + + break; + + /* Make the child exit by sending it a sigkill. */ + case PTRACE_KILL: + ret = 0; + + if (child->state == TASK_ZOMBIE) + break; + + child->exit_code = SIGKILL; + + /* TODO: make sure any pending breakpoint is killed */ + wake_up_process(child); + break; + + /* Set the trap flag. */ + case PTRACE_SINGLESTEP: + ret = -EIO; + + if ((unsigned long) data > _NSIG) + break; + + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + + /* TODO: set some clever breakpoint mechanism... */ + + child->exit_code = data; + wake_up_process(child); + ret = 0; + break; + + case PTRACE_DETACH: + ret = ptrace_detach(child, data); + break; + + /* Get all GP registers from the child. */ + case PTRACE_GETREGS: { + int i; + unsigned long tmp; + + for (i = 0; i <= PT_MAX; i++) { + tmp = get_reg(child, i); + + if (put_user(tmp, (unsigned long *) data)) { + ret = -EFAULT; + break; + } + + data += sizeof(long); + } + + ret = 0; + break; + } + + /* Set all GP registers in the child. */ + case PTRACE_SETREGS: { + int i; + unsigned long tmp; + + for (i = 0; i <= PT_MAX; i++) { + if (get_user(tmp, (unsigned long *) data)) { + ret = -EFAULT; + break; + } + + if (i == PT_DCCR) { + tmp &= DCCR_MASK; + tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK; + } + + put_reg(child, i, tmp); + data += sizeof(long); + } + + ret = 0; + break; + } + + default: + ret = ptrace_request(child, request, addr, data); + break; + } +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +void do_syscall_trace(void) +{ + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + + if (!(current->ptrace & PT_PTRACED)) + return; + + current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0); + + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* + * This isn't the same as continuing with a signal, but it will do for + * normal use. + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff --git a/arch/cris/arch-v10/kernel/setup.c b/arch/cris/arch-v10/kernel/setup.c new file mode 100644 index 000000000000..d95930270a9b --- /dev/null +++ b/arch/cris/arch-v10/kernel/setup.c @@ -0,0 +1,96 @@ +/* $Id: setup.c,v 1.1 2002/12/11 15:42:02 starvik Exp $ + * + * linux/arch/cris/arch-v10/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (c) 2001-2002 Axis Communications AB + */ + +/* + * This file handles the architecture-dependent parts of initialization + */ + +#include <linux/config.h> +#include <linux/seq_file.h> +#include <linux/proc_fs.h> +#include <linux/delay.h> + +#ifdef CONFIG_PROC_FS +#define HAS_FPU 0x0001 +#define HAS_MMU 0x0002 +#define HAS_ETHERNET100 0x0004 +#define HAS_TOKENRING 0x0008 +#define HAS_SCSI 0x0010 +#define HAS_ATA 0x0020 +#define HAS_USB 0x0040 +#define HAS_IRQ_BUG 0x0080 +#define HAS_MMU_BUG 0x0100 + +static struct cpu_info { + char *model; + unsigned short cache; + unsigned short flags; +} cpu_info[] = { + /* The first four models will never ever run this code and are + only here for display. */ + { "ETRAX 1", 0, 0 }, + { "ETRAX 2", 0, 0 }, + { "ETRAX 3", 0, HAS_TOKENRING }, + { "ETRAX 4", 0, HAS_TOKENRING | HAS_SCSI }, + { "Unknown", 0, 0 }, + { "Unknown", 0, 0 }, + { "Unknown", 0, 0 }, + { "Simulator", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA }, + { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG }, + { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA }, + { "ETRAX 100LX", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU | HAS_MMU_BUG }, + { "ETRAX 100LX v2", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU }, + { "Unknown", 0, 0 } /* This entry MUST be the last */ +}; + +int show_cpuinfo(struct seq_file *m, void *v) +{ + unsigned long revision; + struct cpu_info *info; + + /* read the version register in the CPU and print some stuff */ + + revision = rdvr(); + + if (revision >= sizeof cpu_info/sizeof *cpu_info) + info = &cpu_info[sizeof cpu_info/sizeof *cpu_info - 1]; + else + info = &cpu_info[revision]; + + return seq_printf(m, + "processor\t: 0\n" + "cpu\t\t: CRIS\n" + "cpu revision\t: %lu\n" + "cpu model\t: %s\n" + "cache size\t: %d kB\n" + "fpu\t\t: %s\n" + "mmu\t\t: %s\n" + "mmu DMA bug\t: %s\n" + "ethernet\t: %s Mbps\n" + "token ring\t: %s\n" + "scsi\t\t: %s\n" + "ata\t\t: %s\n" + "usb\t\t: %s\n" + "bogomips\t: %lu.%02lu\n", + + revision, + info->model, + info->cache, + info->flags & HAS_FPU ? "yes" : "no", + info->flags & HAS_MMU ? "yes" : "no", + info->flags & HAS_MMU_BUG ? "yes" : "no", + info->flags & HAS_ETHERNET100 ? "10/100" : "10", + info->flags & HAS_TOKENRING ? "4/16 Mbps" : "no", + info->flags & HAS_SCSI ? "yes" : "no", + info->flags & HAS_ATA ? "yes" : "no", + info->flags & HAS_USB ? "yes" : "no", + (loops_per_jiffy * HZ + 500) / 500000, + ((loops_per_jiffy * HZ + 500) / 5000) % 100); +} + +#endif /* CONFIG_PROC_FS */ diff --git a/arch/cris/kernel/shadows.c b/arch/cris/arch-v10/kernel/shadows.c index 25fac64945e4..561a890a8e4c 100644 --- a/arch/cris/kernel/shadows.c +++ b/arch/cris/arch-v10/kernel/shadows.c @@ -1,4 +1,4 @@ -/* $Id: shadows.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: shadows.c,v 1.1 2001/12/17 13:59:27 bjornw Exp $ * * Various shadow registers. Defines for these are in include/asm-etrax100/io.h */ diff --git a/arch/cris/kernel/signal.c b/arch/cris/arch-v10/kernel/signal.c index 678281dae205..6698054a576c 100644 --- a/arch/cris/kernel/signal.c +++ b/arch/cris/arch-v10/kernel/signal.c @@ -55,11 +55,11 @@ sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof, sigset_t saveset; mask &= _BLOCKABLE; - spin_lock_irq(¤t->sigmask_lock); + spin_lock_irq(¤t->sighand->siglock); saveset = current->blocked; siginitset(¤t->blocked, mask); recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sighand->siglock); regs->r10 = -EINTR; while (1) { @@ -94,11 +94,11 @@ sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13, return -EFAULT; sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sigmask_lock); + spin_lock_irq(¤t->sighand->siglock); saveset = current->blocked; current->blocked = newset; recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sighand->siglock); regs->r10 = -EINTR; while (1) { @@ -117,7 +117,7 @@ sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13, } int -sys_sigaction(int sig, const struct old_sigaction *act, +sys_sigaction(int sig, const struct old_sigaction __user *act, struct old_sigaction *oact) { struct k_sigaction new_ka, old_ka; @@ -149,7 +149,7 @@ sys_sigaction(int sig, const struct old_sigaction *act, } int -sys_sigaltstack(const stack_t *uss, stack_t *uoss) +sys_sigaltstack(const stack_t *uss, stack_t __user *uoss) { return do_sigaltstack(uss, uoss, rdusp()); } @@ -175,7 +175,7 @@ struct rt_sigframe { static int -restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) +restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { unsigned int err = 0; unsigned long old_usp; @@ -217,7 +217,7 @@ badframe: asmlinkage int sys_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp, struct pt_regs *regs) { - struct sigframe *frame = (struct sigframe *)rdusp(); + struct sigframe __user *frame = (struct sigframe *)rdusp(); sigset_t set; /* @@ -232,15 +232,15 @@ asmlinkage int sys_sigreturn(long r10, long r11, long r12, long r13, long mof, goto badframe; if (__get_user(set.sig[0], &frame->sc.oldmask) || (_NSIG_WORDS > 1 - && __copy_from_user(&set.sig[1], &frame->extramask, + && __copy_from_user(&set.sig[1], frame->extramask, sizeof(frame->extramask)))) goto badframe; sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sigmask_lock); + spin_lock_irq(¤t->sighand->siglock); current->blocked = set; recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sighand->siglock); if (restore_sigcontext(regs, &frame->sc)) goto badframe; @@ -259,7 +259,7 @@ badframe: asmlinkage int sys_rt_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp, struct pt_regs *regs) { - struct rt_sigframe *frame = (struct rt_sigframe *)rdusp(); + struct rt_sigframe __user *frame = (struct rt_sigframe *)rdusp(); sigset_t set; stack_t st; @@ -277,10 +277,10 @@ asmlinkage int sys_rt_sigreturn(long r10, long r11, long r12, long r13, goto badframe; sigdelsetmask(&set, ~_BLOCKABLE); - spin_lock_irq(¤t->sigmask_lock); + spin_lock_irq(¤t->sighand->siglock); current->blocked = set; recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sighand->siglock); if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) goto badframe; @@ -303,7 +303,7 @@ badframe: */ static int -setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, unsigned long mask) +setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, unsigned long mask) { int err = 0; unsigned long usp = rdusp(); @@ -328,7 +328,7 @@ setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, unsigned long mask /* figure out where we want to put the new signal frame - usually on the stack */ -static inline void * +static inline void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) { unsigned long sp = rdusp(); @@ -343,7 +343,7 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) sp &= ~3; - return (void *)(sp - frame_size); + return (void __user*)(sp - frame_size); } /* grab and setup a signal frame. @@ -357,7 +357,7 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) static void setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs * regs) { - struct sigframe *frame; + struct sigframe __user *frame; unsigned long return_ip; int err = 0; @@ -414,7 +414,7 @@ give_sigsegv: static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs * regs) { - struct rt_sigframe *frame; + struct rt_sigframe __user *frame; unsigned long return_ip; int err = 0; @@ -481,16 +481,18 @@ give_sigsegv: * OK, we're invoking a handler */ -static inline void +extern inline void handle_signal(int canrestart, unsigned long sig, siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) { - struct k_sigaction *ka = ¤t->sig->action[sig-1]; + struct k_sigaction *ka = ¤t->sighand->action[sig-1]; /* Are we from a system call? */ if (canrestart) { /* If so, check system call restarting.. */ switch (regs->r10) { + case -ERESTART_RESTARTBLOCK: + current_thread_info()->restart_block.fn = do_no_restart_syscall; case -ERESTARTNOHAND: /* ERESTARTNOHAND means that the syscall should only be restarted if there was no handler for the signal, and since @@ -523,11 +525,11 @@ handle_signal(int canrestart, unsigned long sig, ka->sa.sa_handler = SIG_DFL; if (!(ka->sa.sa_flags & SA_NODEFER)) { - spin_lock_irq(¤t->sigmask_lock); + spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); sigaddset(¤t->blocked,sig); recalc_sigpending(); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock_irq(¤t->sighand->siglock); } } @@ -575,6 +577,10 @@ int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) regs->r10 == -ERESTARTNOINTR) { RESTART_CRIS_SYS(regs); } + if (regs->r10 == -ERESTART_RESTARTBLOCK){ + regs->r10 = __NR_restart_syscall; + regs->irp -= 2; + } } return 0; } diff --git a/arch/cris/arch-v10/kernel/time.c b/arch/cris/arch-v10/kernel/time.c new file mode 100644 index 000000000000..31e83a84e4a6 --- /dev/null +++ b/arch/cris/arch-v10/kernel/time.c @@ -0,0 +1,359 @@ +/* $Id: time.c,v 1.2 2003/07/04 08:27:41 starvik Exp $ + * + * linux/arch/cris/arch-v10/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * Copyright (C) 1999-2002 Axis Communications AB + * + */ + +#include <linux/config.h> +#include <linux/timex.h> +#include <linux/time.h> +#include <linux/jiffies.h> +#include <linux/interrupt.h> +#include <linux/swap.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <asm/arch/svinto.h> +#include <asm/types.h> +#include <asm/signal.h> +#include <asm/io.h> +#include <asm/delay.h> +#include <asm/rtc.h> + +/* define this if you need to use print_timestamp */ +/* it will make jiffies at 96 hz instead of 100 hz though */ +#undef USE_CASCADE_TIMERS + +extern void update_xtime_from_cmos(void); +extern int set_rtc_mmss(unsigned long nowtime); +extern int setup_irq(int, struct irqaction *); +extern int have_rtc; + +unsigned long get_ns_in_jiffie(void) +{ + unsigned char timer_count, t1; + unsigned short presc_count; + unsigned long ns; + unsigned long flags; + + local_irq_save(flags); + local_irq_disable(); + timer_count = *R_TIMER0_DATA; + presc_count = *R_TIM_PRESC_STATUS; + /* presc_count might be wrapped */ + t1 = *R_TIMER0_DATA; + + if (timer_count != t1){ + /* it wrapped, read prescaler again... */ + presc_count = *R_TIM_PRESC_STATUS; + timer_count = t1; + } + local_irq_restore(flags); + if (presc_count >= PRESCALE_VALUE/2 ){ + presc_count = PRESCALE_VALUE - presc_count + PRESCALE_VALUE/2; + } else { + presc_count = PRESCALE_VALUE - presc_count - PRESCALE_VALUE/2; + } + + ns = ( (TIMER0_DIV - timer_count) * ((1000000000/HZ)/TIMER0_DIV )) + + ( (presc_count) * (1000000000/PRESCALE_FREQ)); + return ns; +} + +unsigned long do_slow_gettimeoffset(void) +{ + unsigned long count, t1; + unsigned long usec_count = 0; + unsigned short presc_count; + + static unsigned long count_p = TIMER0_DIV;/* for the first call after boot */ + static unsigned long jiffies_p = 0; + + /* + * cache volatile jiffies temporarily; we have IRQs turned off. + */ + unsigned long jiffies_t; + + /* The timer interrupt comes from Etrax timer 0. In order to get + * better precision, we check the current value. It might have + * underflowed already though. + */ + +#ifndef CONFIG_SVINTO_SIM + /* Not available in the xsim simulator. */ + count = *R_TIMER0_DATA; + presc_count = *R_TIM_PRESC_STATUS; + /* presc_count might be wrapped */ + t1 = *R_TIMER0_DATA; + if (count != t1){ + /* it wrapped, read prescaler again... */ + presc_count = *R_TIM_PRESC_STATUS; + count = t1; + } +#else + count = 0; + presc_count = 0; +#endif + + jiffies_t = jiffies; + + /* + * avoiding timer inconsistencies (they are rare, but they happen)... + * there are one problem that must be avoided here: + * 1. the timer counter underflows + */ + if( jiffies_t == jiffies_p ) { + if( count > count_p ) { + /* Timer wrapped, use new count and prescale + * increase the time corresponding to one jiffie + */ + usec_count = 1000000/HZ; + } + } else + jiffies_p = jiffies_t; + count_p = count; + if (presc_count >= PRESCALE_VALUE/2 ){ + presc_count = PRESCALE_VALUE - presc_count + PRESCALE_VALUE/2; + } else { + presc_count = PRESCALE_VALUE - presc_count - PRESCALE_VALUE/2; + } + /* Convert timer value to usec */ + usec_count += ( (TIMER0_DIV - count) * (1000000/HZ)/TIMER0_DIV ) + + (( (presc_count) * (1000000000/PRESCALE_FREQ))/1000); + + return usec_count; +} + +/* Excerpt from the Etrax100 HSDD about the built-in watchdog: + * + * 3.10.4 Watchdog timer + + * When the watchdog timer is started, it generates an NMI if the watchdog + * isn't restarted or stopped within 0.1 s. If it still isn't restarted or + * stopped after an additional 3.3 ms, the watchdog resets the chip. + * The watchdog timer is stopped after reset. The watchdog timer is controlled + * by the R_WATCHDOG register. The R_WATCHDOG register contains an enable bit + * and a 3-bit key value. The effect of writing to the R_WATCHDOG register is + * described in the table below: + * + * Watchdog Value written: + * state: To enable: To key: Operation: + * -------- ---------- ------- ---------- + * stopped 0 X No effect. + * stopped 1 key_val Start watchdog with key = key_val. + * started 0 ~key Stop watchdog + * started 1 ~key Restart watchdog with key = ~key. + * started X new_key_val Change key to new_key_val. + * + * Note: '~' is the bitwise NOT operator. + * + */ + +/* right now, starting the watchdog is the same as resetting it */ +#define start_watchdog reset_watchdog + +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) +static int watchdog_key = 0; /* arbitrary number */ +#endif + +/* number of pages to consider "out of memory". it is normal that the memory + * is used though, so put this really low. + */ + +#define WATCHDOG_MIN_FREE_PAGES 8 + +void +reset_watchdog(void) +{ +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) + /* only keep watchdog happy as long as we have memory left! */ + if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) { + /* reset the watchdog with the inverse of the old key */ + watchdog_key ^= 0x7; /* invert key, which is 3 bits */ + *R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, watchdog_key) | + IO_STATE(R_WATCHDOG, enable, start); + } +#endif +} + +/* stop the watchdog - we still need the correct key */ + +void +stop_watchdog(void) +{ +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) + watchdog_key ^= 0x7; /* invert key, which is 3 bits */ + *R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, watchdog_key) | + IO_STATE(R_WATCHDOG, enable, stop); +#endif +} + +/* last time the cmos clock got updated */ +static long last_rtc_update = 0; + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ + +//static unsigned short myjiff; /* used by our debug routine print_timestamp */ + +static inline irqreturn_t +timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* acknowledge the timer irq */ + +#ifdef USE_CASCADE_TIMERS + *R_TIMER_CTRL = + IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) | + IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) | + IO_STATE( R_TIMER_CTRL, i1, clr) | + IO_STATE( R_TIMER_CTRL, tm1, run) | + IO_STATE( R_TIMER_CTRL, clksel1, cascade0) | + IO_STATE( R_TIMER_CTRL, i0, clr) | + IO_STATE( R_TIMER_CTRL, tm0, run) | + IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz); +#else + *R_TIMER_CTRL = r_timer_ctrl_shadow | + IO_STATE(R_TIMER_CTRL, i0, clr); +#endif + + /* reset watchdog otherwise it resets us! */ + + reset_watchdog(); + + /* call the real timer interrupt handler */ + + do_timer(regs); + + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + * + * The division here is not time critical since it will run once in + * 11 minutes + */ + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + (xtime.tv_nsec / 1000) >= 500000 - (tick_nsec / 1000) / 2 && + (xtime.tv_nsec / 1000) <= 500000 + (tick_nsec / 1000) / 2) { + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + } + return IRQ_HANDLED; +} + +/* timer is SA_SHIRQ so drivers can add stuff to the timer irq chain + * it needs to be SA_INTERRUPT to make the jiffies update work properly + */ + +static struct irqaction irq2 = { timer_interrupt, SA_SHIRQ | SA_INTERRUPT, + 0, "timer", NULL, NULL}; + +void __init +time_init(void) +{ + /* probe for the RTC and read it if it exists + * Before the RTC can be probed the loops_per_usec variable needs + * to be initialized to make usleep work. A better value for + * loops_per_usec is calculated by the kernel later once the + * clock has started. + */ + loops_per_usec = 50; + + if(RTC_INIT() < 0) { + /* no RTC, start at 1980 */ + xtime.tv_sec = 0; + xtime.tv_nsec = 0; + have_rtc = 0; + } else { + /* get the current time */ + have_rtc = 1; + update_xtime_from_cmos(); + } + + /* Setup the etrax timers + * Base frequency is 25000 hz, divider 250 -> 100 HZ + * In normal mode, we use timer0, so timer1 is free. In cascade + * mode (which we sometimes use for debugging) both timers are used. + * Remember that linux/timex.h contains #defines that rely on the + * timer settings below (hz and divide factor) !!! + */ + +#ifdef USE_CASCADE_TIMERS + *R_TIMER_CTRL = + IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) | + IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) | + IO_STATE( R_TIMER_CTRL, i1, nop) | + IO_STATE( R_TIMER_CTRL, tm1, stop_ld) | + IO_STATE( R_TIMER_CTRL, clksel1, cascade0) | + IO_STATE( R_TIMER_CTRL, i0, nop) | + IO_STATE( R_TIMER_CTRL, tm0, stop_ld) | + IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz); + + *R_TIMER_CTRL = r_timer_ctrl_shadow = + IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) | + IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) | + IO_STATE( R_TIMER_CTRL, i1, nop) | + IO_STATE( R_TIMER_CTRL, tm1, run) | + IO_STATE( R_TIMER_CTRL, clksel1, cascade0) | + IO_STATE( R_TIMER_CTRL, i0, nop) | + IO_STATE( R_TIMER_CTRL, tm0, run) | + IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz); +#else + *R_TIMER_CTRL = + IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) | + IO_FIELD(R_TIMER_CTRL, timerdiv0, TIMER0_DIV) | + IO_STATE(R_TIMER_CTRL, i1, nop) | + IO_STATE(R_TIMER_CTRL, tm1, stop_ld) | + IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) | + IO_STATE(R_TIMER_CTRL, i0, nop) | + IO_STATE(R_TIMER_CTRL, tm0, stop_ld) | + IO_STATE(R_TIMER_CTRL, clksel0, flexible); + + *R_TIMER_CTRL = r_timer_ctrl_shadow = + IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) | + IO_FIELD(R_TIMER_CTRL, timerdiv0, TIMER0_DIV) | + IO_STATE(R_TIMER_CTRL, i1, nop) | + IO_STATE(R_TIMER_CTRL, tm1, run) | + IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) | + IO_STATE(R_TIMER_CTRL, i0, nop) | + IO_STATE(R_TIMER_CTRL, tm0, run) | + IO_STATE(R_TIMER_CTRL, clksel0, flexible); + + *R_TIMER_PRESCALE = PRESCALE_VALUE; +#endif + + *R_IRQ_MASK0_SET = + IO_STATE(R_IRQ_MASK0_SET, timer0, set); /* unmask the timer irq */ + + /* now actually register the timer irq handler that calls timer_interrupt() */ + + setup_irq(2, &irq2); /* irq 2 is the timer0 irq in etrax */ + + /* enable watchdog if we should use one */ + +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) + printk("Enabling watchdog...\n"); + start_watchdog(); + + /* If we use the hardware watchdog, we want to trap it as an NMI + and dump registers before it resets us. For this to happen, we + must set the "m" NMI enable flag (which once set, is unset only + when an NMI is taken). + + The same goes for the external NMI, but that doesn't have any + driver or infrastructure support yet. */ + asm ("setf m"); + + *R_IRQ_MASK0_SET = + IO_STATE(R_IRQ_MASK0_SET, watchdog_nmi, set); + *R_VECT_MASK_SET = + IO_STATE(R_VECT_MASK_SET, nmi, set); +#endif +} diff --git a/arch/cris/arch-v10/kernel/traps.c b/arch/cris/arch-v10/kernel/traps.c new file mode 100644 index 000000000000..da491f438a6e --- /dev/null +++ b/arch/cris/arch-v10/kernel/traps.c @@ -0,0 +1,132 @@ +/* $Id: traps.c,v 1.2 2003/07/04 08:27:41 starvik Exp $ + * + * linux/arch/cris/arch-v10/traps.c + * + * Heler functions for trap handlers + * + * Copyright (C) 2000-2002 Axis Communications AB + * + * Authors: Bjorn Wesen + * Hans-Peter Nilsson + * + */ + +#include <linux/config.h> +#include <linux/ptrace.h> +#include <asm/uaccess.h> +#include <asm/arch/sv_addr_ag.h> + +void +show_registers(struct pt_regs * regs) +{ + /* We either use rdusp() - the USP register, which might not + correspond to the current process for all cases we're called, + or we use the current->thread.usp, which is not up to date for + the current process. Experience shows we want the USP + register. */ + unsigned long usp = rdusp(); + + printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n", + regs->irp, regs->srp, regs->dccr, usp, regs->mof ); + printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", + regs->r0, regs->r1, regs->r2, regs->r3); + printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + regs->r4, regs->r5, regs->r6, regs->r7); + printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + regs->r8, regs->r9, regs->r10, regs->r11); + printk("r12: %08lx r13: %08lx oR10: %08lx\n", + regs->r12, regs->r13, regs->orig_r10); + printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE); + printk("Process %s (pid: %d, stackpage=%08lx)\n", + current->comm, current->pid, (unsigned long)current); + + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (! user_mode(regs)) { + int i; + + show_stack(NULL, (unsigned long*)usp); + + /* Dump kernel stack if the previous dump wasn't one. */ + if (usp != 0) + show_stack (NULL, NULL); + + printk("\nCode: "); + if(regs->irp < PAGE_OFFSET) + goto bad; + + /* Often enough the value at regs->irp does not point to + the interesting instruction, which is most often the + _previous_ instruction. So we dump at an offset large + enough that instruction decoding should be in sync at + the interesting point, but small enough to fit on a row + (sort of). We point out the regs->irp location in a + ksymoops-friendly way by wrapping the byte for that + address in parentheses. */ + for(i = -12; i < 12; i++) + { + unsigned char c; + if(__get_user(c, &((unsigned char*)regs->irp)[i])) { +bad: + printk(" Bad IP value."); + break; + } + + if (i == 0) + printk("(%02x) ", c); + else + printk("%02x ", c); + } + printk("\n"); + } +} + +/* Called from entry.S when the watchdog has bitten + * We print out something resembling an oops dump, and if + * we have the nice doggy development flag set, we halt here + * instead of rebooting. + */ + +extern void reset_watchdog(void); +extern void stop_watchdog(void); + + +void +watchdog_bite_hook(struct pt_regs *regs) +{ +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + local_irq_disable(); + stop_watchdog(); + show_registers(regs); + while(1) /* nothing */; +#else + show_registers(regs); +#endif +} + +/* This is normally the 'Oops' routine */ +void +die_if_kernel(const char * str, struct pt_regs * regs, long err) +{ + if(user_mode(regs)) + return; + +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + /* This printout might take too long and trigger the + * watchdog normally. If we're in the nice doggy + * development mode, stop the watchdog during printout. + */ + stop_watchdog(); +#endif + + printk("%s: %04lx\n", str, err & 0xffff); + + show_registers(regs); + +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + reset_watchdog(); +#endif + do_exit(SIGSEGV); +} diff --git a/arch/cris/lib/Makefile b/arch/cris/arch-v10/lib/Makefile index 568496a8884e..36e9a9c5239b 100644 --- a/arch/cris/lib/Makefile +++ b/arch/cris/arch-v10/lib/Makefile @@ -2,6 +2,8 @@ # Makefile for Etrax-specific library files.. # + EXTRA_AFLAGS := -traditional lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o + diff --git a/arch/cris/lib/checksum.S b/arch/cris/arch-v10/lib/checksum.S index fd15a76f78d6..85c48f0a9ec2 100644 --- a/arch/cris/lib/checksum.S +++ b/arch/cris/arch-v10/lib/checksum.S @@ -1,4 +1,4 @@ -/* $Id: checksum.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: checksum.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ * A fast checksum routine using movem * Copyright (c) 1998-2001 Axis Communications AB * diff --git a/arch/cris/lib/checksumcopy.S b/arch/cris/arch-v10/lib/checksumcopy.S index 4d8bc348da39..35cbffb306fd 100644 --- a/arch/cris/lib/checksumcopy.S +++ b/arch/cris/arch-v10/lib/checksumcopy.S @@ -1,4 +1,4 @@ -/* $Id: checksumcopy.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: checksumcopy.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ * A fast checksum+copy routine using movem * Copyright (c) 1998, 2001 Axis Communications AB * diff --git a/arch/cris/lib/csumcpfruser.S b/arch/cris/arch-v10/lib/csumcpfruser.S index 5f41ccd62754..5f41ccd62754 100644 --- a/arch/cris/lib/csumcpfruser.S +++ b/arch/cris/arch-v10/lib/csumcpfruser.S diff --git a/arch/cris/lib/dmacopy.c b/arch/cris/arch-v10/lib/dmacopy.c index fe8f091ed09c..e5fb44f505c5 100644 --- a/arch/cris/lib/dmacopy.c +++ b/arch/cris/arch-v10/lib/dmacopy.c @@ -1,4 +1,4 @@ -/* $Id: dmacopy.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: dmacopy.c,v 1.1 2001/12/17 13:59:27 bjornw Exp $ * * memcpy for large blocks, using memory-memory DMA channels 6 and 7 in Etrax */ diff --git a/arch/cris/lib/dram_init.S b/arch/cris/arch-v10/lib/dram_init.S index d46efd1aef1f..4852709dc7aa 100644 --- a/arch/cris/lib/dram_init.S +++ b/arch/cris/arch-v10/lib/dram_init.S @@ -1,4 +1,4 @@ -/* $Id: dram_init.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: dram_init.S,v 1.3 2003/03/31 09:38:37 starvik Exp $ * * DRAM/SDRAM initialization - alter with care * This file is intended to be included from other assembler files @@ -11,8 +11,20 @@ * Authors: Mikael Starvik (starvik@axis.com) * * $Log: dram_init.S,v $ - * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw - * Import of Linux 2.5.1 + * Revision 1.3 2003/03/31 09:38:37 starvik + * Corrected calculation of end of sdram init commands + * + * Revision 1.2 2002/11/19 13:33:29 starvik + * Changes from Linux 2.4 + * + * Revision 1.13 2002/10/30 07:42:28 starvik + * Always read SDRAM command sequence from flash + * + * Revision 1.12 2002/08/09 11:37:37 orjanf + * Added double initialization work-around for Samsung SDRAMs. + * + * Revision 1.11 2002/06/04 11:43:21 starvik + * Check if mrs_data is specified in kernelconfig (necessary for MCM) * * Revision 1.10 2001/10/04 12:00:21 martinnn * Added missing underscores. @@ -73,7 +85,11 @@ move.d CONFIG_ETRAX_DEF_R_DRAM_TIMING, $r0 move.d $r0, [R_DRAM_TIMING] -#else +#else + ;; Samsung SDRAMs seem to require to be initialized twice to work properly. + moveq 2, $r6 +_sdram_init: + ; Refer to ETRAX 100LX Designers Reference for a description of SDRAM initialization ; Bank configuration @@ -85,6 +101,12 @@ ; CAS latency = 3 && bus_width = 32 => 0x60 ; CAS latency = 2 && bus_width = 16 => 0x20 ; CAS latency = 3 && bus_width = 16 => 0x30 + + ; Check if value is already supplied in kernel config + move.d CONFIG_ETRAX_DEF_R_SDRAM_TIMING, $r2 + and.d 0x00ff0000, $r2 + bne _set_timing + lsrq 16, $r2 move.d 0x40, $r2 ; Assume 32 bits and CAS latency = 2 move.d CONFIG_ETRAX_DEF_R_SDRAM_TIMING, $r1 @@ -130,7 +152,9 @@ _set_timing: ; Issue initialization command sequence 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, $r3 1: clear.d $r4 move.b [$r2+], $r4 lslq 9, $r4 ; Command starts at bit 9 @@ -145,6 +169,9 @@ _set_timing: bne 1b nop move.d $r5, [R_SDRAM_TIMING] + subq 1, $r6 + bne _sdram_init + nop ba _sdram_commands_end nop diff --git a/arch/cris/lib/hw_settings.S b/arch/cris/arch-v10/lib/hw_settings.S index b35bb3baa5aa..56905aaa7b6e 100644 --- a/arch/cris/lib/hw_settings.S +++ b/arch/cris/arch-v10/lib/hw_settings.S @@ -1,5 +1,5 @@ /* - * $Id: hw_settings.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ + * $Id: hw_settings.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ * * This table is used by some tools to extract hardware parameters. * The table should be included in the kernel and the decompressor. diff --git a/arch/cris/lib/memset.c b/arch/cris/arch-v10/lib/memset.c index 82bb66839171..82bb66839171 100644 --- a/arch/cris/lib/memset.c +++ b/arch/cris/arch-v10/lib/memset.c diff --git a/arch/cris/lib/old_checksum.c b/arch/cris/arch-v10/lib/old_checksum.c index 5d4d9bc4b057..3b68d1a60a0a 100644 --- a/arch/cris/lib/old_checksum.c +++ b/arch/cris/arch-v10/lib/old_checksum.c @@ -1,4 +1,4 @@ -/* $Id: old_checksum.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: old_checksum.c,v 1.2 2002/11/05 06:45:12 starvik Exp $ * * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket @@ -75,7 +75,7 @@ unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) sum += *((unsigned short *)buff)++; } if(endMarker - buff > 0) { - sum += *buff; /* add extra byte separately */ + sum += *buff; /* add extra byte seperately */ } BITOFF; return(sum); diff --git a/arch/cris/lib/string.c b/arch/cris/arch-v10/lib/string.c index 8ffde4901b57..8ffde4901b57 100644 --- a/arch/cris/lib/string.c +++ b/arch/cris/arch-v10/lib/string.c diff --git a/arch/cris/lib/usercopy.c b/arch/cris/arch-v10/lib/usercopy.c index fc0b89cc57fe..43778d53c254 100644 --- a/arch/cris/lib/usercopy.c +++ b/arch/cris/arch-v10/lib/usercopy.c @@ -31,7 +31,7 @@ kernel-to-kernel copying; see "string.c". */ unsigned long -__copy_user (void *pdst, const void *psrc, unsigned long pn) +__copy_user (void __user *pdst, const void *psrc, unsigned long pn) { /* We want the parameters put in special registers. Make sure the compiler is able to make something useful of this. @@ -88,11 +88,11 @@ __copy_user (void *pdst, const void *psrc, unsigned long pn) If you want to check that the allocation was right; then check the equalities in the first comment. It should say "r13=r13, r11=r11, r12=r12". */ - __asm__ volatile (" - ;; Check that the following is true (same register names on - ;; both sides of equal sign, as in r8=r8): - ;; %0=r13, %1=r11, %2=r12 %3=r10 - ;; + __asm__ volatile ("\ + .ifnc %0%1%2%3,$r13$r11$r12$r10 \n\ + .err \n\ + .endif \n\ + ;; Save the registers we'll use in the movem process ;; on the stack. subq 11*4,$sp @@ -189,10 +189,11 @@ __copy_user (void *pdst, const void *psrc, unsigned long pn) } /* Copy from user to kernel, zeroing the bytes that were inaccessible in - userland. */ + userland. The return-value is the number of bytes that were + inaccessible. */ unsigned long -__copy_user_zeroing (void *pdst, const void *psrc, unsigned long pn) +__copy_user_zeroing (void __user *pdst, const void *psrc, unsigned long pn) { /* We want the parameters put in special registers. Make sure the compiler is able to make something useful of this. @@ -207,30 +208,34 @@ __copy_user_zeroing (void *pdst, const void *psrc, unsigned long pn) register int n __asm__ ("r12") = pn; register int retn __asm__ ("r10") = 0; - /* When src is aligned but not dst, this makes a few extra needless - cycles. I believe it would take as many to check that the - re-alignment was unnecessary. */ - if (((unsigned long) dst & 3) != 0 - /* Don't align if we wouldn't copy more than a few bytes; so we - don't have to check further for overflows. */ - && n >= 3) + /* The best reason to align src is that we then know that a read-fault + was for aligned bytes; there's no 1..3 remaining good bytes to + pickle. */ + if (((unsigned long) src & 3) != 0) { - if ((unsigned long) dst & 1) + if (((unsigned long) src & 1) && n != 0) { __asm_copy_from_user_1 (dst, src, retn); n--; } - if ((unsigned long) dst & 2) + if (((unsigned long) src & 2) && n >= 2) { __asm_copy_from_user_2 (dst, src, retn); n -= 2; } + + /* We only need one check after the unalignment-adjustments, because + if both adjustments were done, either both or neither reference + had an exception. */ + if (retn != 0) + goto copy_exception_bytes; } /* Decide which copying method to use. */ if (n >= 44*2) /* Break even between movem and - move16 is at 38.7*2, but modulo 44. */ + move16 is at 38.7*2, but modulo 44. + FIXME: We use move4 now. */ { /* For large copies we use 'movem' */ @@ -249,10 +254,10 @@ __copy_user_zeroing (void *pdst, const void *psrc, unsigned long pn) check the equalities in the first comment. It should say "r13=r13, r11=r11, r12=r12" */ __asm__ volatile (" - ;; Check that the following is true (same register names on - ;; both sides of equal sign, as in r8=r8): - ;; %0=r13, %1=r11, %2=r12 %3=r10 - ;; + .ifnc %0%1%2%3,$r13$r11$r12$r10 \n\ + .err \n\ + .endif \n\ + ;; Save the registers we'll use in the movem process ;; on the stack. subq 11*4,$sp @@ -273,73 +278,30 @@ __copy_user_zeroing (void *pdst, const void *psrc, unsigned long pn) movem $r10,[$r13+] addq 44,$r12 ;; compensate for last loop underflowing n -8: + ;; Restore registers from stack movem [$sp+],$r10 - +4: .section .fixup,\"ax\" ;; Do not jump back into the loop if we fail. For some uses, we get a -;; page fault but for performance reasons we care to not get further -;; faults. For example, fs/super.c at one time did +;; page fault somewhere on the line. Without checking for page limits, +;; we don't know where, but we need to copy accurately and keep an +;; accurate count; not just clear the whole line. To do that, we fall +;; down in the code below, proceeding with smaller amounts. It should +;; be kept in mind that we have to cater to code like what at one time +;; was in fs/super.c: ;; i = size - copy_from_user((void *)page, data, size); ;; which would cause repeated faults while clearing the remainder of ;; the SIZE bytes at PAGE after the first fault. +;; A caveat here is that we must not fall through from a failing page +;; to a valid page. 3: - move.d [$sp],$r10 - -;; Number of remaining bytes, cleared but not copied, is r12 + 44. - - add.d $r12,$r10 - addq 44,$r10 - - move.d $r10,[$sp] - clear.d $r0 - clear.d $r1 - clear.d $r2 - clear.d $r3 - clear.d $r4 - clear.d $r5 - clear.d $r6 - clear.d $r7 - clear.d $r8 - clear.d $r9 - clear.d $r10 - -;; Perform clear similar to the copy-loop. - -4: - subq 44,$r12 - bge 4b - movem $r10,[$r13+] - -;; Clear by four for the remaining multiples. - - addq 40,$r12 - bmi 6f - nop -5: - subq 4,$r12 - bpl 5b - clear.d [$r13+] -6: - addq 4,$r12 - beq 7f - nop - - subq 1,$r12 - beq 7f - clear.b [$r13+] - - subq 1,$r12 - beq 7f - clear.b [$r13+] - - clear.d $r12 - clear.b [$r13+] -7: - jump 8b + movem [$sp+],$r10 + addq 44,$r12 ;; Get back count before faulting point. + subq 44,$r11 ;; Get back pointer to faulting movem-line. + jump 4b ;; Fall through, pretending the fault didn't happen. .previous .section __ex_table,\"a\" @@ -354,25 +316,30 @@ __copy_user_zeroing (void *pdst, const void *psrc, unsigned long pn) /* Either we directly start copying here, using dword copying in a loop, or we copy as much as possible with 'movem' and then the last block (<44 bytes) is copied here. This will work since 'movem' will have - updated src, dst and n. */ + updated src, dst and n. (Except with failing src.) - while (n >= 16) - { - __asm_copy_from_user_16 (dst, src, retn); - n -= 16; - } + Since we want to keep src accurate, we can't use + __asm_copy_from_user_N with N != (1, 2, 4); it updates dst and + retn, but not src (by design; it's value is ignored elsewhere). */ - /* Having a separate by-four loops cuts down on cache footprint. - FIXME: Test with and without; increasing switch to be 0..15. */ while (n >= 4) { __asm_copy_from_user_4 (dst, src, retn); n -= 4; + + if (retn) + goto copy_exception_bytes; } + /* If we get here, there were no memory read faults. */ switch (n) { + /* These copies are at least "naturally aligned" (so we don't have + to check each byte), due to the src alignment code before the + movem loop. The *_3 case *will* get the correct count for retn. */ case 0: + /* This case deliberately left in (if you have doubts check the + generated assembly code). */ break; case 1: __asm_copy_from_user_1 (dst, src, retn); @@ -385,13 +352,28 @@ __copy_user_zeroing (void *pdst, const void *psrc, unsigned long pn) break; } + /* If we get here, retn correctly reflects the number of failing + bytes. */ return retn; + +copy_exception_bytes: + /* We already have "retn" bytes cleared, and need to clear the + remaining "n" bytes. A non-optimized simple byte-for-byte in-line + memset is preferred here, since this isn't speed-critical code and + we'd rather have this a leaf-function than calling memset. */ + { + char *endp; + for (endp = dst + n; dst < endp; dst++) + *dst = 0; + } + + return retn + n; } /* Zero userspace. */ unsigned long -__do_clear_user (void *pto, unsigned long pn) +__do_clear_user (void __user *pto, unsigned long pn) { /* We want the parameters put in special registers. Make sure the compiler is able to make something useful of this. @@ -444,10 +426,10 @@ __do_clear_user (void *pto, unsigned long pn) check the equalities in the first comment. It should say something like "r13=r13, r11=r11, r12=r12". */ __asm__ volatile (" - ;; Check that the following is true (same register names on - ;; both sides of equal sign, as in r8=r8): - ;; %0=r13, %1=r12 %2=r10 - ;; + .ifnc %0%1%2,$r13$r12$r10 \n\ + .err \n\ + .endif \n\ + ;; Save the registers we'll clobber in the movem process ;; on the stack. Don't mention them to gcc, it will only be ;; upset. diff --git a/arch/cris/arch-v10/mm/Makefile b/arch/cris/arch-v10/mm/Makefile new file mode 100644 index 000000000000..588b4baee85e --- /dev/null +++ b/arch/cris/arch-v10/mm/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the linux cris-specific parts of the memory manager. +# + +obj-y := fault.o init.o tlb.o + diff --git a/arch/cris/arch-v10/mm/fault.c b/arch/cris/arch-v10/mm/fault.c new file mode 100644 index 000000000000..7a012adc1579 --- /dev/null +++ b/arch/cris/arch-v10/mm/fault.c @@ -0,0 +1,175 @@ +/* + * linux/arch/cris/mm/fault.c + * + * Low level bus fault handler + * + * + * Copyright (C) 2000, 2001 Axis Communications AB + * + * Authors: Bjorn Wesen + * + */ + +#include <linux/mm.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/arch/svinto.h> + +/* debug of low-level TLB reload */ +#undef DEBUG + +#ifdef DEBUG +#define D(x) x +#else +#define D(x) +#endif + +extern volatile pgd_t *current_pgd; + +extern const struct exception_table_entry + *search_exception_tables(unsigned long addr); + +asmlinkage void do_page_fault(unsigned long address, struct pt_regs *regs, + int error_code); + +/* fast TLB-fill fault handler + * this is called from entry.S with interrupts disabled + */ + +void +handle_mmu_bus_fault(struct pt_regs *regs) +{ + int cause, select; +#ifdef DEBUG + int index; + int page_id; + int acc, inv; +#endif + int miss, we, writeac; + pmd_t *pmd; + pte_t pte; + int errcode; + unsigned long address; + + cause = *R_MMU_CAUSE; + select = *R_TLB_SELECT; + + address = cause & PAGE_MASK; /* get faulting address */ + +#ifdef DEBUG + page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause); + acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause); + inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause); + index = IO_EXTRACT(R_TLB_SELECT, index, select); +#endif + miss = IO_EXTRACT(R_MMU_CAUSE, miss_excp, cause); + we = IO_EXTRACT(R_MMU_CAUSE, we_excp, cause); + writeac = IO_EXTRACT(R_MMU_CAUSE, wr_rd, cause); + + /* ETRAX 100LX TR89 bugfix: if the second half of an unaligned + * write causes a MMU-fault, it will not be restarted correctly. + * This could happen if a write crosses a page-boundary and the + * second page is not yet COW'ed or even loaded. The workaround + * is to clear the unaligned bit in the CPU status record, so + * that the CPU will rerun both the first and second halves of + * the instruction. This will not have any sideeffects unless + * the first half goes to any device or memory that can't be + * written twice, and which is mapped through the MMU. + * + * We only need to do this for writes. + */ + + if(writeac) + regs->csrinstr &= ~(1 << 5); + + /* Set errcode's R/W flag according to the mode which caused the + * fault + */ + + errcode = writeac << 1; + + D(printk("bus_fault from IRP 0x%lx: addr 0x%lx, miss %d, inv %d, we %d, acc %d, dx %d pid %d\n", + regs->irp, address, miss, inv, we, acc, index, page_id)); + + /* for a miss, we need to reload the TLB entry */ + + if (miss) { + /* see if the pte exists at all + * refer through current_pgd, dont use mm->pgd + */ + + pmd = (pmd_t *)(current_pgd + pgd_index(address)); + if (pmd_none(*pmd)) + goto dofault; + if (pmd_bad(*pmd)) { + printk("bad pgdir entry 0x%lx at 0x%p\n", *(unsigned long*)pmd, pmd); + pmd_clear(pmd); + return; + } + pte = *pte_offset_kernel(pmd, address); + if (!pte_present(pte)) + goto dofault; + +#ifdef DEBUG + printk(" found pte %lx pg %p ", pte_val(pte), pte_page(pte)); + if (pte_val(pte) & _PAGE_SILENT_WRITE) + printk("Silent-W "); + if (pte_val(pte) & _PAGE_KERNEL) + printk("Kernel "); + if (pte_val(pte) & _PAGE_SILENT_READ) + printk("Silent-R "); + if (pte_val(pte) & _PAGE_GLOBAL) + printk("Global "); + if (pte_val(pte) & _PAGE_PRESENT) + printk("Present "); + if (pte_val(pte) & _PAGE_ACCESSED) + printk("Accessed "); + if (pte_val(pte) & _PAGE_MODIFIED) + printk("Modified "); + if (pte_val(pte) & _PAGE_READ) + printk("Readable "); + if (pte_val(pte) & _PAGE_WRITE) + printk("Writeable "); + printk("\n"); +#endif + + /* load up the chosen TLB entry + * this assumes the pte format is the same as the TLB_LO layout. + * + * the write to R_TLB_LO also writes the vpn and page_id fields from + * R_MMU_CAUSE, which we in this case obviously want to keep + */ + + *R_TLB_LO = pte_val(pte); + + return; + } + + errcode = 1 | (we << 1); + + dofault: + /* leave it to the MM system fault handler below */ + D(printk("do_page_fault %lx errcode %d\n", address, errcode)); + do_page_fault(address, regs, errcode); +} + +/* Called from arch/cris/mm/fault.c to find fixup code. */ +int +find_fixup_code(struct pt_regs *regs) +{ + const struct exception_table_entry *fixup; + + if ((fixup = search_exception_tables(regs->irp)) != 0) { + /* Adjust the instruction pointer in the stackframe. */ + regs->irp = fixup->fixup; + + /* + * Don't return by restoring the CPU state, so switch + * frame-type. + */ + regs->frametype = CRIS_FRAME_NORMAL; + return 1; + } + + return 0; +} diff --git a/arch/cris/arch-v10/mm/init.c b/arch/cris/arch-v10/mm/init.c new file mode 100644 index 000000000000..df03dea5bae4 --- /dev/null +++ b/arch/cris/arch-v10/mm/init.c @@ -0,0 +1,265 @@ +/* + * linux/arch/cris/arch-v10/mm/init.c + * + */ +#include <linux/config.h> +#include <linux/mmzone.h> +#include <linux/init.h> +#include <linux/bootmem.h> +#include <linux/mm.h> +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/types.h> +#include <asm/mmu.h> +#include <asm/io.h> +#include <asm/mmu_context.h> +#include <asm/arch/svinto.h> + +extern void tlb_init(void); + +/* + * The kernel is already mapped with a kernel segment at kseg_c so + * we don't need to map it with a page table. However head.S also + * temporarily mapped it at kseg_4 so we should set up the ksegs again, + * clear the TLB and do some other paging setup stuff. + */ + +void __init +paging_init(void) +{ + int i; + unsigned long zones_size[MAX_NR_ZONES]; + + printk("Setting up paging and the MMU.\n"); + + /* clear out the init_mm.pgd that will contain the kernel's mappings */ + + for(i = 0; i < PTRS_PER_PGD; i++) + swapper_pg_dir[i] = __pgd(0); + + /* make sure the current pgd table points to something sane + * (even if it is most probably not used until the next + * switch_mm) + */ + + current_pgd = init_mm.pgd; + + /* initialise the TLB (tlb.c) */ + + tlb_init(); + + /* see README.mm for details on the KSEG setup */ + +#ifdef CONFIG_CRIS_LOW_MAP + /* Etrax-100 LX version 1 has a bug so that we cannot map anything + * across the 0x80000000 boundary, so we need to shrink the user-virtual + * area to 0x50000000 instead of 0xb0000000 and map things slightly + * different. The unused areas are marked as paged so that we can catch + * freak kernel accesses there. + * + * The ARTPEC chip is mapped at 0xa so we pass that segment straight + * through. We cannot vremap it because the vmalloc area is below 0x8 + * and Juliette needs an uncached area above 0x8. + * + * Same thing with 0xc and 0x9, which is memory-mapped I/O on some boards. + * We map them straight over in LOW_MAP, but use vremap in LX version 2. + */ + +#define CACHED_BOOTROM (KSEG_F | 0x08000000UL) + + *R_MMU_KSEG = ( IO_STATE(R_MMU_KSEG, seg_f, seg ) | /* bootrom */ + IO_STATE(R_MMU_KSEG, seg_e, page ) | + IO_STATE(R_MMU_KSEG, seg_d, page ) | + IO_STATE(R_MMU_KSEG, seg_c, page ) | + IO_STATE(R_MMU_KSEG, seg_b, seg ) | /* kernel reg area */ +#ifdef CONFIG_JULIETTE + IO_STATE(R_MMU_KSEG, seg_a, seg ) | /* ARTPEC etc. */ +#else + IO_STATE(R_MMU_KSEG, seg_a, page ) | +#endif + IO_STATE(R_MMU_KSEG, seg_9, seg ) | /* LED's on some boards */ + IO_STATE(R_MMU_KSEG, seg_8, seg ) | /* CSE0/1, flash and I/O */ + IO_STATE(R_MMU_KSEG, seg_7, page ) | /* kernel vmalloc area */ + IO_STATE(R_MMU_KSEG, seg_6, seg ) | /* kernel DRAM area */ + IO_STATE(R_MMU_KSEG, seg_5, seg ) | /* cached flash */ + IO_STATE(R_MMU_KSEG, seg_4, page ) | /* user area */ + IO_STATE(R_MMU_KSEG, seg_3, page ) | /* user area */ + IO_STATE(R_MMU_KSEG, seg_2, page ) | /* user area */ + IO_STATE(R_MMU_KSEG, seg_1, page ) | /* user area */ + IO_STATE(R_MMU_KSEG, seg_0, page ) ); /* user area */ + + *R_MMU_KBASE_HI = ( IO_FIELD(R_MMU_KBASE_HI, base_f, 0x3 ) | + IO_FIELD(R_MMU_KBASE_HI, base_e, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_d, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_c, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_b, 0xb ) | +#ifdef CONFIG_JULIETTE + IO_FIELD(R_MMU_KBASE_HI, base_a, 0xa ) | +#else + IO_FIELD(R_MMU_KBASE_HI, base_a, 0x0 ) | +#endif + IO_FIELD(R_MMU_KBASE_HI, base_9, 0x9 ) | + IO_FIELD(R_MMU_KBASE_HI, base_8, 0x8 ) ); + + *R_MMU_KBASE_LO = ( IO_FIELD(R_MMU_KBASE_LO, base_7, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_6, 0x4 ) | + IO_FIELD(R_MMU_KBASE_LO, base_5, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_4, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_3, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_2, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_1, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_0, 0x0 ) ); +#else + /* This code is for the corrected Etrax-100 LX version 2... */ + +#define CACHED_BOOTROM (KSEG_A | 0x08000000UL) + + *R_MMU_KSEG = ( IO_STATE(R_MMU_KSEG, seg_f, seg ) | /* cached flash */ + IO_STATE(R_MMU_KSEG, seg_e, seg ) | /* uncached flash */ + IO_STATE(R_MMU_KSEG, seg_d, page ) | /* vmalloc area */ + IO_STATE(R_MMU_KSEG, seg_c, seg ) | /* kernel area */ + IO_STATE(R_MMU_KSEG, seg_b, seg ) | /* kernel reg area */ + IO_STATE(R_MMU_KSEG, seg_a, seg ) | /* bootrom */ + IO_STATE(R_MMU_KSEG, seg_9, page ) | /* user area */ + IO_STATE(R_MMU_KSEG, seg_8, page ) | + IO_STATE(R_MMU_KSEG, seg_7, page ) | + IO_STATE(R_MMU_KSEG, seg_6, page ) | + IO_STATE(R_MMU_KSEG, seg_5, page ) | + IO_STATE(R_MMU_KSEG, seg_4, page ) | + IO_STATE(R_MMU_KSEG, seg_3, page ) | + IO_STATE(R_MMU_KSEG, seg_2, page ) | + IO_STATE(R_MMU_KSEG, seg_1, page ) | + IO_STATE(R_MMU_KSEG, seg_0, page ) ); + + *R_MMU_KBASE_HI = ( IO_FIELD(R_MMU_KBASE_HI, base_f, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_e, 0x8 ) | + IO_FIELD(R_MMU_KBASE_HI, base_d, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_c, 0x4 ) | + IO_FIELD(R_MMU_KBASE_HI, base_b, 0xb ) | + IO_FIELD(R_MMU_KBASE_HI, base_a, 0x3 ) | + IO_FIELD(R_MMU_KBASE_HI, base_9, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_8, 0x0 ) ); + + *R_MMU_KBASE_LO = ( IO_FIELD(R_MMU_KBASE_LO, base_7, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_6, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_5, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_4, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_3, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_2, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_1, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_0, 0x0 ) ); +#endif + + *R_MMU_CONTEXT = ( IO_FIELD(R_MMU_CONTEXT, page_id, 0 ) ); + + /* The MMU has been enabled ever since head.S but just to make + * it totally obvious we do it here as well. + */ + + *R_MMU_CTRL = ( IO_STATE(R_MMU_CTRL, inv_excp, enable ) | + IO_STATE(R_MMU_CTRL, acc_excp, enable ) | + IO_STATE(R_MMU_CTRL, we_excp, enable ) ); + + *R_MMU_ENABLE = IO_STATE(R_MMU_ENABLE, mmu_enable, enable); + + /* + * initialize the bad page table and bad page to point + * to a couple of allocated pages + */ + + empty_zero_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE); + memset((void *)empty_zero_page, 0, PAGE_SIZE); + + /* All pages are DMA'able in Etrax, so put all in the DMA'able zone */ + + zones_size[0] = ((unsigned long)high_memory - PAGE_OFFSET) >> PAGE_SHIFT; + + for (i = 1; i < MAX_NR_ZONES; i++) + zones_size[i] = 0; + + /* Use free_area_init_node instead of free_area_init, because the former + * is designed for systems where the DRAM starts at an address substantially + * higher than 0, like us (we start at PAGE_OFFSET). This saves space in the + * mem_map page array. + */ + + free_area_init_node(0, &contig_page_data, 0, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); + mem_map = contig_page_data.node_mem_map; +} + +/* Initialize remaps of some I/O-ports. It is important that this + * is called before any driver is initialized. + */ + +static int +__init init_ioremap(void) +{ + + /* Give the external I/O-port addresses their values */ + +#ifdef CONFIG_CRIS_LOW_MAP + /* Simply a linear map (see the KSEG map above in paging_init) */ + port_cse1_addr = (volatile unsigned long *)(MEM_CSE1_START | + MEM_NON_CACHEABLE); + port_csp0_addr = (volatile unsigned long *)(MEM_CSP0_START | + MEM_NON_CACHEABLE); + port_csp4_addr = (volatile unsigned long *)(MEM_CSP4_START | + MEM_NON_CACHEABLE); +#else + /* Note that nothing blows up just because we do this remapping + * it's ok even if the ports are not used or connected + * to anything (or connected to a non-I/O thing) */ + port_cse1_addr = (volatile unsigned long *) + ioremap((unsigned long)(MEM_CSE1_START | MEM_NON_CACHEABLE), 16); + port_csp0_addr = (volatile unsigned long *) + ioremap((unsigned long)(MEM_CSP0_START | MEM_NON_CACHEABLE), 16); + port_csp4_addr = (volatile unsigned long *) + ioremap((unsigned long)(MEM_CSP4_START | MEM_NON_CACHEABLE), 16); +#endif + return 0; +} + +__initcall(init_ioremap); + +/* Helper function for the two below */ + +static inline void +flush_etrax_cacherange(void *startadr, int length) +{ + /* CACHED_BOOTROM is mapped to the boot-rom area (cached) which + * we can use to get fast dummy-reads of cachelines + */ + + volatile short *flushadr = (volatile short *)(((unsigned long)startadr & ~PAGE_MASK) | + CACHED_BOOTROM); + + length = length > 8192 ? 8192 : length; /* No need to flush more than cache size */ + + while(length > 0) { + *flushadr; /* dummy read to flush */ + flushadr += (32/sizeof(short)); /* a cacheline is 32 bytes */ + length -= 32; + } +} + +/* Due to a bug in Etrax100(LX) all versions, receiving DMA buffers + * will occationally corrupt certain CPU writes if the DMA buffers + * happen to be hot in the cache. + * + * As a workaround, we have to flush the relevant parts of the cache + * before (re) inserting any receiving descriptor into the DMA HW. + */ + +void +prepare_rx_descriptor(struct etrax_dma_descr *desc) +{ + flush_etrax_cacherange((void *)desc->buf, desc->sw_len ? desc->sw_len : 65536); +} + +/* Do the same thing but flush the entire cache */ + +void +flush_etrax_cache(void) +{ + flush_etrax_cacherange(0, 8192); +} diff --git a/arch/cris/arch-v10/mm/tlb.c b/arch/cris/arch-v10/mm/tlb.c new file mode 100644 index 000000000000..2ddb0a61cdbf --- /dev/null +++ b/arch/cris/arch-v10/mm/tlb.c @@ -0,0 +1,236 @@ +/* + * linux/arch/cris/arch-v10/mm/tlb.c + * + * Low level TLB handling + * + * + * Copyright (C) 2000-2002 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + */ + +#include <asm/tlb.h> +#include <asm/mmu_context.h> +#include <asm/arch/svinto.h> + +#define D(x) + +/* The TLB can host up to 64 different mm contexts at the same time. + * The running context is R_MMU_CONTEXT, and each TLB entry contains a + * page_id that has to match to give a hit. In page_id_map, we keep track + * of which mm's we have assigned which page_id's, so that we know when + * to invalidate TLB entries. + * + * The last page_id is never running - it is used as an invalid page_id + * so we can make TLB entries that will never match. + * + * Notice that we need to make the flushes atomic, otherwise an interrupt + * handler that uses vmalloced memory might cause a TLB load in the middle + * of a flush causing. + */ + +/* invalidate all TLB entries */ + +void +flush_tlb_all(void) +{ + int i; + unsigned long flags; + + /* the vpn of i & 0xf is so we dont write similar TLB entries + * in the same 4-way entry group. details.. + */ + + local_save_flags(flags); + local_irq_disable(); + for(i = 0; i < NUM_TLB_ENTRIES; i++) { + *R_TLB_SELECT = ( IO_FIELD(R_TLB_SELECT, index, i) ); + *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | + IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); + + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | + IO_STATE(R_TLB_LO, valid, no ) | + IO_STATE(R_TLB_LO, kernel,no ) | + IO_STATE(R_TLB_LO, we, no ) | + IO_FIELD(R_TLB_LO, pfn, 0 ) ); + } + local_irq_restore(flags); + D(printk("tlb: flushed all\n")); +} + +/* invalidate the selected mm context only */ + +void +flush_tlb_mm(struct mm_struct *mm) +{ + int i; + int page_id = mm->context; + unsigned long flags; + + D(printk("tlb: flush mm context %d (%p)\n", page_id, mm)); + + if(page_id == NO_CONTEXT) + return; + + /* mark the TLB entries that match the page_id as invalid. + * here we could also check the _PAGE_GLOBAL bit and NOT flush + * global pages. is it worth the extra I/O ? + */ + + local_save_flags(flags); + local_irq_disable(); + for(i = 0; i < NUM_TLB_ENTRIES; i++) { + *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i); + if (IO_EXTRACT(R_TLB_HI, page_id, *R_TLB_HI) == page_id) { + *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | + IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); + + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | + IO_STATE(R_TLB_LO, valid, no ) | + IO_STATE(R_TLB_LO, kernel,no ) | + IO_STATE(R_TLB_LO, we, no ) | + IO_FIELD(R_TLB_LO, pfn, 0 ) ); + } + } + local_irq_restore(flags); +} + +/* invalidate a single page */ + +void +flush_tlb_page(struct vm_area_struct *vma, + unsigned long addr) +{ + struct mm_struct *mm = vma->vm_mm; + int page_id = mm->context; + int i; + unsigned long flags; + + D(printk("tlb: flush page %p in context %d (%p)\n", addr, page_id, mm)); + + if(page_id == NO_CONTEXT) + return; + + addr &= PAGE_MASK; /* perhaps not necessary */ + + /* invalidate those TLB entries that match both the mm context + * and the virtual address requested + */ + + local_save_flags(flags); + local_irq_disable(); + for(i = 0; i < NUM_TLB_ENTRIES; i++) { + unsigned long tlb_hi; + *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i); + tlb_hi = *R_TLB_HI; + if (IO_EXTRACT(R_TLB_HI, page_id, tlb_hi) == page_id && + (tlb_hi & PAGE_MASK) == addr) { + *R_TLB_HI = IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | + addr; /* same addr as before works. */ + + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | + IO_STATE(R_TLB_LO, valid, no ) | + IO_STATE(R_TLB_LO, kernel,no ) | + IO_STATE(R_TLB_LO, we, no ) | + IO_FIELD(R_TLB_LO, pfn, 0 ) ); + } + } + local_irq_restore(flags); +} + +/* invalidate a page range */ + +void +flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, + unsigned long end) +{ + struct mm_struct *mm = vma->vm_mm; + int page_id = mm->context; + int i; + unsigned long flags; + + D(printk("tlb: flush range %p<->%p in context %d (%p)\n", + start, end, page_id, mm)); + + if(page_id == NO_CONTEXT) + return; + + start &= PAGE_MASK; /* probably not necessary */ + end &= PAGE_MASK; /* dito */ + + /* invalidate those TLB entries that match both the mm context + * and the virtual address range + */ + + local_save_flags(flags); + local_irq_disable(); + for(i = 0; i < NUM_TLB_ENTRIES; i++) { + unsigned long tlb_hi, vpn; + *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i); + tlb_hi = *R_TLB_HI; + vpn = tlb_hi & PAGE_MASK; + if (IO_EXTRACT(R_TLB_HI, page_id, tlb_hi) == page_id && + vpn >= start && vpn < end) { + *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | + IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); + + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | + IO_STATE(R_TLB_LO, valid, no ) | + IO_STATE(R_TLB_LO, kernel,no ) | + IO_STATE(R_TLB_LO, we, no ) | + IO_FIELD(R_TLB_LO, pfn, 0 ) ); + } + } + local_irq_restore(flags); +} + +/* dump the entire TLB for debug purposes */ + +#if 0 +void +dump_tlb_all(void) +{ + int i; + unsigned long flags; + + printk("TLB dump. LO is: pfn | reserved | global | valid | kernel | we |\n"); + + local_save_flags(flags); + local_irq_disable(); + for(i = 0; i < NUM_TLB_ENTRIES; i++) { + *R_TLB_SELECT = ( IO_FIELD(R_TLB_SELECT, index, i) ); + printk("Entry %d: HI 0x%08lx, LO 0x%08lx\n", + i, *R_TLB_HI, *R_TLB_LO); + } + local_irq_restore(flags); +} +#endif + +/* called in schedule() just before actually doing the switch_to */ + +void +switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk, int cpu) +{ + /* make sure we have a context */ + + get_mmu_context(next); + + /* remember the pgd for the fault handlers + * this is similar to the pgd register in some other CPU's. + * we need our own copy of it because current and active_mm + * might be invalid at points where we still need to derefer + * the pgd. + */ + + current_pgd = next->pgd; + + /* switch context in the MMU */ + + D(printk("switching mmu_context to %d (%p)\n", next->context, next)); + + *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT, page_id, next->context); +} + diff --git a/arch/cris/arch-v10/output_arch.ld b/arch/cris/arch-v10/output_arch.ld new file mode 100644 index 000000000000..2f3288006991 --- /dev/null +++ b/arch/cris/arch-v10/output_arch.ld @@ -0,0 +1,2 @@ +/* At the time of this writing, there's no equivalent ld option. */ +OUTPUT_ARCH (cris) diff --git a/arch/cris/vmlinux.lds.S b/arch/cris/arch-v10/vmlinux.lds.S index 3a5f1a36da38..b2c27e147f29 100644 --- a/arch/cris/vmlinux.lds.S +++ b/arch/cris/arch-v10/vmlinux.lds.S @@ -10,11 +10,11 @@ #include <linux/config.h> #include <asm-generic/vmlinux.lds.h> - + jiffies = jiffies_64; SECTIONS { - . = 0x ## CONFIG_ETRAX_DRAM_VIRTUAL_BASE; + . = DRAM_VIRTUAL_BASE; dram_start = .; ibr_start = .; . = . + 0x4000; /* see head.S and pages reserved at the start */ @@ -53,12 +53,19 @@ SECTIONS . = ALIGN(8192); /* Init code and data */ __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + .init.data : { *(.init.data) } . = ALIGN(16); __setup_start = .; - .setup.init : { *(.setup.init) } + .init.setup : { *(.init.setup) } __setup_end = .; + __start___param = .; + __param : { *(__param) } + __stop___param = .; .initcall.init : { __initcall_start = .; *(.initcall1.init); @@ -68,24 +75,27 @@ SECTIONS *(.initcall5.init); *(.initcall6.init); *(.initcall7.init); - __initcall_end = .; + __initcall_end = .; } + .con_initcall.init : { __con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .; - } - .security_initcall.init : { - __security_initcall_start = .; - *(.security_initcall.init) - __security_initcall_end = .; - + } + SECURITY_INIT + + .init.ramfs : { + __initramfs_start = .; + *(.init.ramfs) + __initramfs_end = .; /* We fill to the next page, so we can discard all init pages without needing to consider what payload might be appended to the kernel image. */ - FILL (0); + FILL (0); . = ALIGN (8192); } + __vmlinux_end = .; /* last address of the physical file */ __init_end = .; diff --git a/arch/cris/defconfig b/arch/cris/defconfig index 9d40dd316576..7af178b78328 100644 --- a/arch/cris/defconfig +++ b/arch/cris/defconfig @@ -27,93 +27,13 @@ CONFIG_BINFMT_ELF=y CONFIG_ETRAX100LX=y # CONFIG_ETRAX100LX_V2 is not set # CONFIG_SVINTO_SIM is not set -CONFIG_CRIS_LOW_MAP=y -CONFIG_ETRAX_DRAM_VIRTUAL_BASE=60000000 CONFIG_ETRAX_DRAM_SIZE=8 CONFIG_ETRAX_FLASH_BUSWIDTH=2 CONFIG_ETRAX_ROOT_DEVICE="/dev/mtdblock3" -CONFIG_ETRAX_PA_LEDS=y -# CONFIG_ETRAX_PB_LEDS is not set -# CONFIG_ETRAX_CSP0_LEDS is not set -# CONFIG_ETRAX_NO_LEDS is not set -CONFIG_ETRAX_LED1G=2 -CONFIG_ETRAX_LED1R=2 -CONFIG_ETRAX_LED2G=2 -CONFIG_ETRAX_LED2R=2 -CONFIG_ETRAX_LED3R=2 -CONFIG_ETRAX_LED3G=2 -CONFIG_ETRAX_LED4R=2 -CONFIG_ETRAX_LED4G=2 -CONFIG_ETRAX_LED5R=2 -CONFIG_ETRAX_LED5G=2 -CONFIG_ETRAX_LED6R=2 -CONFIG_ETRAX_LED6G=2 -CONFIG_ETRAX_LED7R=2 -CONFIG_ETRAX_LED7G=2 -CONFIG_ETRAX_LED8Y=2 -CONFIG_ETRAX_LED9Y=2 -CONFIG_ETRAX_LED10Y=2 -CONFIG_ETRAX_LED11Y=2 -CONFIG_ETRAX_LED12R=2 -CONFIG_ETRAX_DEBUG_PORT0=y -# CONFIG_ETRAX_DEBUG_PORT1 is not set -# CONFIG_ETRAX_DEBUG_PORT2 is not set -# CONFIG_ETRAX_DEBUG_PORT3 is not set -CONFIG_ETRAX_RESCUE_SER0=y -# CONFIG_ETRAX_RESCUE_SER1 is not set -# CONFIG_ETRAX_RESCUE_SER2 is not set -# CONFIG_ETRAX_RESCUE_SER3 is not set -CONFIG_ETRAX_DEF_R_WAITSTATES=95a6 -CONFIG_ETRAX_DEF_R_BUS_CONFIG=104 -# CONFIG_ETRAX_SDRAM is not set -CONFIG_ETRAX_DEF_R_DRAM_CONFIG=1a200040 -CONFIG_ETRAX_DEF_R_DRAM_TIMING=5611 -CONFIG_ETRAX_DEF_R_PORT_PA_DIR=1d -CONFIG_ETRAX_DEF_R_PORT_PA_DATA=f0 -CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG=00 -CONFIG_ETRAX_DEF_R_PORT_PB_DIR=1e -CONFIG_ETRAX_DEF_R_PORT_PB_DATA=f3 -# CONFIG_ETRAX_SOFT_SHUTDOWN is not set # # Drivers for ETRAX 100LX built-in interfaces # -CONFIG_ETRAX_ETHERNET=y -CONFIG_NET_ETHERNET=y -# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set -CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y -# CONFIG_ETRAX_ETHERNET_LPSLAVE is not set -CONFIG_ETRAX_SERIAL=y -CONFIG_ETRAX_SERIAL_PORT0=y -# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB is not set -CONFIG_ETRAX_SERIAL_PORT1=y -# CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB is not set -# CONFIG_ETRAX_SERIAL_PORT2 is not set -# CONFIG_ETRAX_SERIAL_PORT3 is not set -# CONFIG_ETRAX_RS485 is not set -# CONFIG_ETRAX_SYNCHRONOUS_SERIAL is not set -# CONFIG_ETRAX_IDE is not set -CONFIG_ETRAX_AXISFLASHMAP=y -CONFIG_ETRAX_PTABLE_SECTOR=65536 -CONFIG_MTD=y -CONFIG_MTD_CFI=y -# CONFIG_MTD_CFI_INTELEXT is not set -CONFIG_MTD_CFI_AMDSTD=y -CONFIG_MTD_AMDSTD=y -CONFIG_MTD_CHAR=y -CONFIG_MTD_BLOCK=y -CONFIG_ETRAX_I2C=y -CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C=y -# CONFIG_ETRAX_I2C_EEPROM is not set -CONFIG_ETRAX_GPIO=y -CONFIG_ETRAX_PA_BUTTON_BITMASK=02 -CONFIG_ETRAX_PA_CHANGEABLE_DIR=00 -CONFIG_ETRAX_PA_CHANGEABLE_BITS=FF -CONFIG_ETRAX_PB_CHANGEABLE_DIR=00 -CONFIG_ETRAX_PB_CHANGEABLE_BITS=FF -# CONFIG_ETRAX_USB_HOST is not set -# CONFIG_USB is not set -# CONFIG_ETRAX_DS1302 is not set # # Memory Technology Devices (MTD) diff --git a/arch/cris/drivers/axisflashmap.c b/arch/cris/drivers/axisflashmap.c deleted file mode 100644 index 6db4078e068b..000000000000 --- a/arch/cris/drivers/axisflashmap.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Physical mapping layer for MTD using the Axis partitiontable format - * - * Copyright (c) 2001 Axis Communications AB - * - * This file is under the GPL. - * - * First partition is always sector 0 regardless of if we find a partitiontable - * or not. In the start of the next sector, there can be a partitiontable that - * tells us what other partitions to define. If there isn't, we use a default - * partition split defined below. - * - * $Log: axisflashmap.c,v $ - * Revision 1.2 2001/12/18 13:35:15 bjornw - * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). - * - * Revision 1.17 2001/11/12 19:42:38 pkj - * Fixed compiler warnings. - * - * Revision 1.16 2001/11/08 11:18:58 jonashg - * Always read from uncached address to avoid problems with flushing - * cachelines after write and MTD-erase. No performance loss have been - * seen yet. - * - * Revision 1.15 2001/10/19 12:41:04 jonashg - * Name of probe has changed in MTD. - * - * Revision 1.14 2001/09/21 07:14:10 jonashg - * Made root filesystem (cramfs) use mtdblock driver when booting from flash. - * - * Revision 1.13 2001/08/15 13:57:35 jonashg - * Entire MTD updated to the linux 2.4.7 version. - * - * Revision 1.12 2001/06/11 09:50:30 jonashg - * Oops, 2MB is 0x200000 bytes. - * - * Revision 1.11 2001/06/08 11:39:44 jonashg - * Changed sizes and offsets in axis_default_partitions to use - * CONFIG_ETRAX_PTABLE_SECTOR. - * - * Revision 1.10 2001/05/29 09:42:03 jonashg - * Use macro for end marker length instead of sizeof. - * - * Revision 1.9 2001/05/29 08:52:52 jonashg - * Gave names to the magic fours (size of the ptable end marker). - * - * Revision 1.8 2001/05/28 15:36:20 jonashg - * * Removed old comment about ptable location in flash (it's a CONFIG_ option). - * * Variable ptable was initialized twice to the same value. - * - * Revision 1.7 2001/04/05 13:41:46 markusl - * Updated according to review remarks - * - * Revision 1.6 2001/03/07 09:21:21 bjornw - * No need to waste .data - * - * Revision 1.5 2001/03/06 16:27:01 jonashg - * Probe the entire flash area for flash devices. - * - * Revision 1.4 2001/02/23 12:47:15 bjornw - * Uncached flash in LOW_MAP moved from 0xe to 0x8 - * - * Revision 1.3 2001/02/16 12:11:45 jonashg - * MTD driver amd_flash is now included in MTD CVS repository. - * (It's now in drivers/mtd). - * - * Revision 1.2 2001/02/09 11:12:22 jonashg - * Support for AMD compatible non-CFI flash chips. - * Only tested with Toshiba TC58FVT160 so far. - * - * Revision 1.1 2001/01/12 17:01:18 bjornw - * * Added axisflashmap.c, a physical mapping for MTD that reads and understands - * Axis partition-table format. - * - * - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/config.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - -#include <asm/axisflashmap.h> -#include <asm/mmu.h> - -#ifdef CONFIG_CRIS_LOW_MAP -#define FLASH_UNCACHED_ADDR KSEG_8 -#define FLASH_CACHED_ADDR KSEG_5 -#else -#define FLASH_UNCACHED_ADDR KSEG_E -#define FLASH_CACHED_ADDR KSEG_F -#endif - -/* - * WINDOW_SIZE is the total size where the flash chips may be mapped. - * MTD probes should find all devices there and it does not matter - * if there are unmapped gaps or aliases (mirrors of flash devices). - * The MTD probes will ignore them. - */ - -#define WINDOW_SIZE (128 * 1024 * 1024) - -extern unsigned long romfs_start, romfs_length, romfs_in_flash; /* From head.S */ - -/* - * Map driver - * - * Ok this is the scoop - we need to access the flash both with and without - * the cache - without when doing all the fancy flash interfacing, and with - * when we do actual copying because otherwise it will be slow like molasses. - */ - -static __u8 flash_read8(struct map_info *map, unsigned long ofs) -{ - return *(__u8 *)(FLASH_UNCACHED_ADDR + ofs); -} - -static __u16 flash_read16(struct map_info *map, unsigned long ofs) -{ - return *(__u16 *)(FLASH_UNCACHED_ADDR + ofs); -} - -static __u32 flash_read32(struct map_info *map, unsigned long ofs) -{ - return *(volatile unsigned int *)(FLASH_UNCACHED_ADDR + ofs); -} - -static void flash_copy_from(struct map_info *map, void *to, - unsigned long from, ssize_t len) -{ - memcpy(to, (void *)(FLASH_UNCACHED_ADDR + from), len); -} - -static void flash_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - *(__u8 *)(FLASH_UNCACHED_ADDR + adr) = d; -} - -static void flash_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - *(__u16 *)(FLASH_UNCACHED_ADDR + adr) = d; -} - -static void flash_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - *(__u32 *)(FLASH_UNCACHED_ADDR + adr) = d; -} - -static struct map_info axis_map = { - .name = "Axis flash", - .size = WINDOW_SIZE, - .buswidth = CONFIG_ETRAX_FLASH_BUSWIDTH, - .read8 = flash_read8, - .read16 = flash_read16, - .read32 = flash_read32, - .copy_from = flash_copy_from, - .write8 = flash_write8, - .write16 = flash_write16, - .write32 = flash_write32, -}; - -/* If no partition-table was found, we use this default-set. - */ - -#define MAX_PARTITIONS 7 -#define NUM_DEFAULT_PARTITIONS 3 - -/* Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the - * size of one flash block and "filesystem"-partition needs 5 blocks to be able - * to use JFFS. - */ -static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { - { - .name = "boot firmware", - .size = CONFIG_ETRAX_PTABLE_SECTOR, - .offset = 0 - }, - { - .name = "kernel", - .size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR), - .offset = CONFIG_ETRAX_PTABLE_SECTOR - }, - { - .name = "filesystem", - .size = 5 * CONFIG_ETRAX_PTABLE_SECTOR, - .offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR) - } -}; - -static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { - { - .name = "part0", - .size = 0, - .offset = 0 - }, - { - .name = "part1", - .size = 0, - .offset = 0 - }, - { - .name = "part2", - .size = 0, - .offset = 0 - }, - { - .name = "part3", - .size = 0, - .offset = 0 - }, - { - .name = "part4", - .size = 0, - .offset = 0 - }, - { - .name = "part5", - .size = 0, - .offset = 0 - }, - { - .name = "part6", - .size = 0, - .offset = 0 - }, -}; - -/* - * This is the master MTD device for which all the others are just - * auto-relocating aliases. - */ -static struct mtd_info *mymtd; - -/* CFI-scan the flash, and if there was a chip, read the partition-table - * and register the partitions with MTD. - */ - -static int __init -init_axis_flash(void) -{ - int pidx = 0; - 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%x, size 0x%x\n"; - - printk(KERN_NOTICE "Axis flash mapping: %x at %lx\n", - WINDOW_SIZE, FLASH_CACHED_ADDR); - -#ifdef CONFIG_MTD_CFI - mymtd = (struct mtd_info *)do_map_probe("cfi_probe", &axis_map); -#endif - -#ifdef CONFIG_MTD_AMDSTD - if (!mymtd) { - mymtd = (struct mtd_info *)do_map_probe("amd_flash", &axis_map); - } -#endif - - if(!mymtd) { - printk("%s: No flash chip found!\n", axis_map.name); - return -ENXIO; - } - - mymtd->module = THIS_MODULE; - - ptable_head = (struct partitiontable_head *)(FLASH_CACHED_ADDR + - CONFIG_ETRAX_PTABLE_SECTOR + PARTITION_TABLE_OFFSET); - pidx++; /* first partition is always set to the default */ - - if ((ptable_head->magic == PARTITION_TABLE_MAGIC) - && (ptable_head->size < - (MAX_PARTITIONS * sizeof(struct partitiontable_entry) + - PARTITIONTABLE_END_MARKER_SIZE)) - && (*(unsigned long*)((void*)ptable_head + sizeof(*ptable_head) + - ptable_head->size - - PARTITIONTABLE_END_MARKER_SIZE) - == PARTITIONTABLE_END_MARKER)) { - /* Looks like a start, sane length and end of a - * partition table, lets check csum etc. - */ - int ptable_ok = 0; - struct partitiontable_entry *max_addr = - (struct partitiontable_entry *) - ((unsigned long)ptable_head + sizeof(*ptable_head) + - ptable_head->size); - unsigned long offset = CONFIG_ETRAX_PTABLE_SECTOR; - unsigned char *p; - unsigned long csum = 0; - - ptable = (struct partitiontable_entry *) - ((unsigned long)ptable_head + sizeof(*ptable_head)); - - /* Lets be PARANOID, and check the checksum. */ - p = (unsigned char*) ptable; - - while (p <= (unsigned char*)max_addr) { - csum += *p++; - csum += *p++; - csum += *p++; - csum += *p++; - } - /* printk(" total csum: 0x%08X 0x%08X\n", - csum, ptable_head->checksum); */ - ptable_ok = (csum == ptable_head->checksum); - - /* Read the entries and use/show the info. */ - printk(" Found %s partition table at 0x%08lX-0x%08lX.\n", - (ptable_ok ? "valid" : "invalid"), - (unsigned long)ptable_head, - (unsigned long)max_addr); - - /* We have found a working bootblock. Now read the - partition table. Scan the table. It ends when - there is 0xffffffff, that is, empty flash. */ - - while (ptable_ok - && ptable->offset != 0xffffffff - && ptable < max_addr - && pidx < MAX_PARTITIONS) { - - axis_partitions[pidx].offset = offset + ptable->offset; - axis_partitions[pidx].size = ptable->size; - - printk(pmsg, pidx, axis_partitions[pidx].offset, - axis_partitions[pidx].size); - pidx++; - ptable++; - } - use_default_ptable = !ptable_ok; - } - - if (use_default_ptable) { - printk(" Using default partition table\n"); - return add_mtd_partitions(mymtd, axis_default_partitions, - NUM_DEFAULT_PARTITIONS); - } else { - if (romfs_in_flash) { - axis_partitions[pidx].name = "romfs"; - axis_partitions[pidx].size = romfs_length; - axis_partitions[pidx].offset = romfs_start - - FLASH_CACHED_ADDR; - axis_partitions[pidx].mask_flags |= MTD_WRITEABLE; - - printk(" Adding readonly partition for romfs image:\n"); - printk(pmsg, pidx, axis_partitions[pidx].offset, - axis_partitions[pidx].size); - pidx++; - } - return add_mtd_partitions(mymtd, axis_partitions, pidx); - } -} - -/* This adds the above to the kernels init-call chain */ - -module_init(init_axis_flash); - diff --git a/arch/cris/drivers/bluetooth/Makefile b/arch/cris/drivers/bluetooth/Makefile deleted file mode 100644 index b3cbffc6c1a4..000000000000 --- a/arch/cris/drivers/bluetooth/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -include $(APPS)/Rules.elinux - -all: - -install: src/bluetooth.c include/btcommon.h - ln -sfn ../../arch/cris/drivers/bluetooth/include ../../../../include/linux/bluetooth - if ! grep arch/cris/drivers/bluetooth/src/Config.in ../Config.in; then \ - echo '' >> ../Config.in; \ - echo 'if [ "$$CONFIG_ETRAX_SERIAL" = "y" ]; then' >> ../Config.in; \ - echo ' source arch/cris/drivers/bluetooth/src/Config.in' >> ../Config.in; \ - echo 'fi' >> ../Config.in; \ - fi - if ! grep bluetooth/src/bt.o ../Makefile; then \ - perl -pi -e "s:include:obj-\\\$$(CONFIG_BLUETOOTH) += bluetooth/src/bt.o\nsubdir-\\\$$(CONFIG_BLUETOOTH) += bluetooth/src\n\ninclude:" ../Makefile; \ - fi - -clean: - -src/bluetooth.c: - @echo "You must install the OpenBT src directory before install can be done here!" - @exit 1 - -include/btcommon.h: - @echo "You must install the OpenBT include directory before install can be done here!" - @exit 1 diff --git a/arch/cris/drivers/gpio.c b/arch/cris/drivers/gpio.c deleted file mode 100644 index 533e2738c46f..000000000000 --- a/arch/cris/drivers/gpio.c +++ /dev/null @@ -1,460 +0,0 @@ -/* $Id: gpio.c,v 1.2 2001/12/18 13:35:15 bjornw Exp $ - * - * Etrax general port I/O device - * - * Copyright (c) 1999, 2000, 2001 Axis Communications AB - * - * Authors: Bjorn Wesen (initial version) - * Ola Knutsson (LED handling) - * Johan Adolfsson (read/set directions, write) - * - * $Log: gpio.c,v $ - * Revision 1.2 2001/12/18 13:35:15 bjornw - * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). - * - * Revision 1.12 2001/11/12 19:42:15 pkj - * * Corrected return values from gpio_leds_ioctl(). - * * Fixed compiler warnings. - * - * Revision 1.11 2001/10/30 14:39:12 johana - * Added D() around gpio_write printk. - * - * Revision 1.10 2001/10/25 10:24:42 johana - * Added IO_CFG_WRITE_MODE ioctl and write method that can do fast - * bittoggling in the kernel. (This speeds up programming an FPGA with 450kB - * from ~60 seconds to 4 seconds). - * Added save_flags/cli/restore_flags in ioctl. - * - * Revision 1.9 2001/05/04 14:16:07 matsfg - * Corrected spelling error - * - * Revision 1.8 2001/04/27 13:55:26 matsfg - * Moved initioremap. - * Turns off all LEDS on init. - * Added support for shutdown and powerbutton. - * - * Revision 1.7 2001/04/04 13:30:08 matsfg - * Added bitset and bitclear for leds. Calls init_ioremap to set up memmapping - * - * Revision 1.6 2001/03/26 16:03:06 bjornw - * Needs linux/config.h - * - * Revision 1.5 2001/03/26 14:22:03 bjornw - * Namechange of some config options - * - * Revision 1.4 2001/02/27 13:52:48 bjornw - * malloc.h -> slab.h - * - * Revision 1.3 2001/01/24 15:06:48 bjornw - * gpio_wq correct type - * - * Revision 1.2 2001/01/18 16:07:30 bjornw - * 2.4 port - * - * Revision 1.1 2001/01/18 15:55:16 bjornw - * Verbatim copy of etraxgpio.c from elinux 2.0 added - * - * - */ - -#include <linux/config.h> - -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/ioport.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/string.h> -#include <linux/poll.h> -#include <linux/init.h> - -#include <asm/etraxgpio.h> -#include <asm/svinto.h> -#include <asm/io.h> -#include <asm/system.h> - -#define GPIO_MAJOR 120 /* experimental MAJOR number */ - -#define D(x) - -static char gpio_name[] = "etrax gpio"; - -#if 0 -static wait_queue_head_t *gpio_wq; -#endif - -static int gpio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off); -static int gpio_open(struct inode *inode, struct file *filp); -static int gpio_release(struct inode *inode, struct file *filp); -static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait); - -/* private data per open() of this driver */ - -struct gpio_private { - struct gpio_private *next; - volatile unsigned char *port, *shadow; - volatile unsigned char *dir, *dir_shadow; - unsigned char changeable_dir; - unsigned char changeable_bits; - unsigned char highalarm, lowalarm; - unsigned char clk_mask; - unsigned char data_mask; - unsigned char write_msb; - wait_queue_head_t alarm_wq; - int minor; -}; - -/* linked list of alarms to check for */ - -static struct gpio_private *alarmlist = 0; - -#define NUM_PORTS 2 -static volatile unsigned char *ports[2] = { R_PORT_PA_DATA, R_PORT_PB_DATA }; -static volatile unsigned char *shads[2] = { - &port_pa_data_shadow, &port_pb_data_shadow }; - -/* What direction bits that are user changeable 1=changeable*/ -#ifndef CONFIG_ETRAX_PA_CHANGEABLE_DIR -#define CONFIG_ETRAX_PA_CHANGEABLE_DIR 0x00 -#endif -#ifndef CONFIG_ETRAX_PB_CHANGEABLE_DIR -#define CONFIG_ETRAX_PB_CHANGEABLE_DIR 0x00 -#endif - -#ifndef CONFIG_ETRAX_PA_CHANGEABLE_BITS -#define CONFIG_ETRAX_PA_CHANGEABLE_BITS 0xFF -#endif -#ifndef CONFIG_ETRAX_PB_CHANGEABLE_BITS -#define CONFIG_ETRAX_PB_CHANGEABLE_BITS 0xFF -#endif - - -static unsigned char changeable_dir[2] = { CONFIG_ETRAX_PA_CHANGEABLE_DIR, - CONFIG_ETRAX_PB_CHANGEABLE_DIR }; -static unsigned char changeable_bits[2] = { CONFIG_ETRAX_PA_CHANGEABLE_BITS, - CONFIG_ETRAX_PB_CHANGEABLE_BITS }; - -static volatile unsigned char *dir[2] = { R_PORT_PA_DIR, R_PORT_PB_DIR }; - -static volatile unsigned char *dir_shadow[2] = { - &port_pa_dir_shadow, &port_pb_dir_shadow }; - -#define LEDS 2 - -static unsigned int -gpio_poll(struct file *filp, - struct poll_table_struct *wait) -{ - /* TODO poll on alarms! */ -#if 0 - if (!ANYTHING_WANTED) { - D(printk("gpio_select sleeping task\n")); - select_wait(&gpio_wq, table); - return 0; - } - D(printk("gpio_select ready\n")); -#endif - return 1; -} - -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off) -{ - struct gpio_private *priv = (struct gpio_private *)file->private_data; - unsigned char data, clk_mask, data_mask, write_msb; - unsigned long flags; - ssize_t retval = count; - if (verify_area(VERIFY_READ, buf, count)) { - return -EFAULT; - } - clk_mask = priv->clk_mask; - data_mask = priv->data_mask; - /* It must have been configured using the IO_CFG_WRITE_MODE */ - /* Perhaps a better error code? */ - if (clk_mask == 0 || data_mask == 0) { - return -EPERM; - } - write_msb = priv->write_msb; - D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb)); - while (count--) { - int i; - data = *buf++; - if (priv->write_msb) { - for (i = 7; i >= 0;i--) { - save_flags(flags); cli(); - *priv->port = *priv->shadow &= ~clk_mask; - if (data & 1<<i) - *priv->port = *priv->shadow |= data_mask; - else - *priv->port = *priv->shadow &= ~data_mask; - /* For FPGA: min 5.0ns (DCC) before CCLK high */ - *priv->port = *priv->shadow |= clk_mask; - restore_flags(flags); - } - } else { - for (i = 0; i <= 7;i++) { - save_flags(flags); cli(); - *priv->port = *priv->shadow &= ~clk_mask; - if (data & 1<<i) - *priv->port = *priv->shadow |= data_mask; - else - *priv->port = *priv->shadow &= ~data_mask; - /* For FPGA: min 5.0ns (DCC) before CCLK high */ - *priv->port = *priv->shadow |= clk_mask; - restore_flags(flags); - } - } - } - return retval; -} - -static int -gpio_open(struct inode *inode, struct file *filp) -{ - struct gpio_private *priv; - int p = minor(inode->i_rdev); - - if (p >= NUM_PORTS && p != LEDS) - return -EINVAL; - - priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private), - GFP_KERNEL); - - if (!priv) - return -ENOMEM; - - priv->minor = p; - - /* initialize the io/alarm struct and link it into our alarmlist */ - - priv->next = alarmlist; - alarmlist = priv; - priv->port = ports[p]; - priv->shadow = shads[p]; - - priv->changeable_dir = changeable_dir[p]; - priv->changeable_bits = changeable_bits[p]; - priv->dir = dir[p]; - priv->dir_shadow = dir_shadow[p]; - - priv->highalarm = 0; - priv->lowalarm = 0; - priv->clk_mask = 0; - priv->data_mask = 0; - init_waitqueue_head(&priv->alarm_wq); - - filp->private_data = (void *)priv; - - return 0; -} - -static int -gpio_release(struct inode *inode, struct file *filp) -{ - struct gpio_private *p = alarmlist; - struct gpio_private *todel = (struct gpio_private *)filp->private_data; - - /* unlink from alarmlist and free the private structure */ - - if (p == todel) { - alarmlist = todel->next; - } else { - while (p->next != todel) - p = p->next; - p->next = todel->next; - } - - kfree(todel); - - return 0; -} - -/* Main device API. ioctl's to read/set/clear bits, as well as to - * set alarms to wait for using a subsequent select(). - */ - -static int -gpio_leds_ioctl(unsigned int cmd, unsigned long arg); - -static int -gpio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - struct gpio_private *priv = (struct gpio_private *)file->private_data; - if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { - return -EINVAL; - } - - switch (_IOC_NR(cmd)) { - case IO_READBITS: - // read the port - return *priv->port; - case IO_SETBITS: - save_flags(flags); cli(); - // set changeable bits with a 1 in arg - *priv->port = *priv->shadow |= - ((unsigned char)arg & priv->changeable_bits); - restore_flags(flags); - break; - case IO_CLRBITS: - save_flags(flags); cli(); - // clear changeable bits with a 1 in arg - *priv->port = *priv->shadow &= - ~((unsigned char)arg & priv->changeable_bits); - restore_flags(flags); - break; - case IO_HIGHALARM: - // set alarm when bits with 1 in arg go high - priv->highalarm |= (unsigned char)arg; - break; - case IO_LOWALARM: - // set alarm when bits with 1 in arg go low - priv->lowalarm |= (unsigned char)arg; - break; - case IO_CLRALARM: - // clear alarm for bits with 1 in arg - priv->highalarm &= ~(unsigned char)arg; - priv->lowalarm &= ~(unsigned char)arg; - break; - case IO_READDIR: - /* Read direction 0=input 1=output */ - return *priv->dir_shadow; - case IO_SETINPUT: - save_flags(flags); cli(); - /* Set direction 0=unchanged 1=input */ - *priv->dir = *priv->dir_shadow &= - ~((unsigned char)arg & priv->changeable_dir); - restore_flags(flags); - return *priv->dir_shadow; - case IO_SETOUTPUT: - save_flags(flags); cli(); - /* Set direction 0=unchanged 1=output */ - *priv->dir = *priv->dir_shadow |= - ((unsigned char)arg & priv->changeable_dir); - restore_flags(flags); - return *priv->dir_shadow; - case IO_SHUTDOWN: - SOFT_SHUTDOWN(); - break; - case IO_GET_PWR_BT: -#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN) - return (*R_PORT_G_DATA & - ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT)); -#else - return 0; -#endif - break; - case IO_CFG_WRITE_MODE: - priv->clk_mask = arg & 0xFF; - priv->data_mask = (arg >> 8) & 0xFF; - priv->write_msb = (arg >> 16) & 0x01; - /* Check if we're allowed to change the bits and - * the direction is correct - */ - if (!((priv->clk_mask & priv->changeable_bits) && - (priv->data_mask & priv->changeable_bits) && - (priv->clk_mask & *priv->dir_shadow) && - (priv->data_mask & *priv->dir_shadow))) - { - priv->clk_mask = 0; - priv->data_mask = 0; - return -EPERM; - } - break; - default: - if (priv->minor == LEDS) - return gpio_leds_ioctl(cmd, arg); - else - return -EINVAL; - } - - return 0; -} - -static int -gpio_leds_ioctl(unsigned int cmd, unsigned long arg) -{ - unsigned char green; - unsigned char red; - - switch (_IOC_NR(cmd)) { - case IO_LEDACTIVE_SET: - green = ((unsigned char) arg) & 1; - red = (((unsigned char) arg) >> 1) & 1; - LED_ACTIVE_SET_G(green); - LED_ACTIVE_SET_R(red); - break; - - case IO_LED_SETBIT: - LED_BIT_SET(arg); - break; - - case IO_LED_CLRBIT: - LED_BIT_CLR(arg); - break; - - default: - return -EINVAL; - } - - return 0; -} - -struct file_operations gpio_fops = { - .owner = THIS_MODULE, - .poll = gpio_poll, - .ioctl = gpio_ioctl, - .write = gpio_write, - .open = gpio_open, - .release = gpio_release, -}; - -/* main driver initialization routine, called from mem.c */ - -static __init int -gpio_init(void) -{ - extern void init_ioremap(void); - int res; -#if defined (CONFIG_ETRAX_CSP0_LEDS) - int i; -#endif - - /* do the formalities */ - - res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); - if (res < 0) { - printk(KERN_ERR "gpio: couldn't get a major number.\n"); - return res; - } - - /* Clear all leds */ -#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) - init_ioremap(); - LED_NETWORK_SET(0); - LED_ACTIVE_SET(0); - LED_DISK_READ(0); - LED_DISK_WRITE(0); - -#if defined (CONFIG_ETRAX_CSP0_LEDS) - for (i = 0; i < 32; i++) { - LED_BIT_SET(i); - } -#endif - -#endif - - printk("ETRAX 100LX GPIO driver v2.2, (c) 2001 Axis Communications AB\n"); - - return res; -} - -/* this makes sure that gpio_init is called during kernel boot */ - -module_init(gpio_init); diff --git a/arch/cris/drivers/ide.c b/arch/cris/drivers/ide.c deleted file mode 100644 index 89ae840968e8..000000000000 --- a/arch/cris/drivers/ide.c +++ /dev/null @@ -1,877 +0,0 @@ -/* $Id: ide.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw 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. - * Copyright (c) 2000, 2001 Axis Communications AB - * - * Authors: Bjorn Wesen (initial version) - * Mikael Starvik (pio setup stuff) - * - * $Log: ide.c,v $ - * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw - * Import of Linux 2.5.1 - * - * Revision 1.19 2001/05/09 12:53:16 johana - * Added #include <asm/dma.h> - * - * Revision 1.18 2001/05/09 12:37:00 johana - * Use DMA_NBR macros from dma.h. - * - * Revision 1.17 2001/04/23 13:36:30 matsfg - * Changed CONFIG_IDE_DELAY to CONFIG_ETRAX_IDE_DELAY - * - * Revision 1.16 2001/04/05 08:30:07 matsfg - * Corrected cse1 and csp0 reset. - * - * Revision 1.15 2001/04/04 14:34:06 bjornw - * Re-instated code that mysteriously disappeared during review updates. - * - * Revision 1.14 2001/04/04 13:45:12 matsfg - * Calls REG_SHADOW_SET for cse1 reset so only the resetbit is affected - * - * Revision 1.13 2001/04/04 13:26:40 matsfg - * memmapping is done in init.c - * - * Revision 1.12 2001/04/04 11:37:56 markusl - * Updated according to review remarks - * - * Revision 1.11 2001/03/29 12:49:14 matsfg - * Changed check for ata_tot_size from >= to >. - * Sets sw_len to 0 if size is exactly 65536. - * - * Revision 1.10 2001/03/16 09:39:30 matsfg - * Support for reset on port CSP0 - * - * Revision 1.9 2001/03/01 13:11:18 bjornw - * 100 -> HZ - * - * Revision 1.8 2001/03/01 09:32:56 matsfg - * Moved IDE delay to a CONFIG-parameter instead - * - * Revision 1.7 2001/02/23 13:46:38 bjornw - * Spellling check - * - * Revision 1.6 2001/02/22 15:44:30 bjornw - * * Use ioremap when mapping the CSE1 memory-mapped reset-line for LX v2 - * * sw_len for a 65536 descriptor is 0, not 65536 - * * Express concern for G27 reset code - * - * Revision 1.5 2001/02/16 07:35:38 matsfg - * Now handles DMA request blocks between 64k and 128k by split into two descriptors. - * - * Revision 1.4 2001/01/10 21:14:32 bjornw - * Initialize hwif->ideproc, for the new way of handling ide_xxx_data - * - * Revision 1.3 2000/12/01 17:48:18 bjornw - * - atapi_output_bytes now uses DMA - * - dma_active check removed - the kernel does proper serializing and it had - * a race-condition anyway - * - ide_build_dmatable had a nameclash - * - re-added the RESET_DMA thingys because sometimes the interface can get - * stuck apparently - * - added ide_release_dma - * - * Revision 1.2 2000/11/29 17:31:29 bjornw - * 2.4 port - * - * - The "register addresses" stored in the hwif are now 32-bit fields that - * don't need to be shifted into correct positions in R_ATA_CTRL_DATA - * - PIO-mode detection temporarily disabled since ide-modes.c is not compiled - * - All DMA uses virt_to_phys conversions for DMA buffers and descriptor ptrs - * - Probably correct ide_dma_begin semantics in dmaproc now for ATAPI devices - * - Removed RESET_DMA when starting a new transfer - why was this necessary ? - * - Indentation fix - * - * - */ - -/* Regarding DMA: - * - * There are two forms of DMA - "DMA handshaking" between the interface and the drive, - * and DMA between the memory and the interface. We can ALWAYS use the latter, since it's - * something built-in in the Etrax. However only some drives support the DMA-mode handshaking - * on the ATA-bus. The normal PC driver and Triton interface disables memory-if DMA when the - * device can't do DMA handshaking for some stupid reason. We don't need to do that. - */ - -#include <linux/config.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/timer.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/blkdev.h> -#include <linux/hdreg.h> -#include <linux/ide.h> -#include <linux/init.h> - -#include <asm/io.h> -#include <asm/svinto.h> -#include <asm/dma.h> - -/* number of Etrax DMA descriptors */ -#define MAX_DMA_DESCRS 64 - -#ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET -/* address where the memory-mapped IDE reset bit lives, if used */ -static volatile unsigned long *reset_addr; -#endif - -#define LOWDB(x) -#define D(x) - -void OUT_BYTE(unsigned char data, ide_ioreg_t reg) { - LOWDB(printk("ob: data 0x%x, reg 0x%x\n", data, reg)); - while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag */ - *R_ATA_CTRL_DATA = reg | data; /* write data to the drive's register */ - while(!(*R_ATA_STATUS_DATA & - IO_MASK(R_ATA_STATUS_DATA, tr_rdy))); /* wait for transmitter ready */ -} - -unsigned char IN_BYTE(ide_ioreg_t reg) { - int status; - while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag */ - *R_ATA_CTRL_DATA = reg | IO_STATE(R_ATA_CTRL_DATA, rw, read); /* read data */ - while(!((status = *R_ATA_STATUS_DATA) & - IO_MASK(R_ATA_STATUS_DATA, dav))); /* wait for available */ - LOWDB(printk("inb: 0x%x from reg 0x%x\n", status & 0xff, reg)); - return (unsigned char)status; /* data was in the lower 16 bits in the status reg */ -} - -/* PIO timing (in R_ATA_CONFIG) - * - * _____________________________ - * ADDRESS : ________/ - * - * _______________ - * DIOR : ____________/ \__________ - * - * _______________ - * DATA : XXXXXXXXXXXXXXXX_______________XXXXXXXX - * - * - * DIOR is unbuffered while address and data is buffered. - * This creates two problems: - * 1. The DIOR pulse is to early (because it is unbuffered) - * 2. The rise time of DIOR is long - * - * There are at least three different plausible solutions - * 1. Use a pad capable of larger currents in Etrax - * 2. Use an external buffer - * 3. Make the strobe pulse longer - * - * Some of the strobe timings below are modified to compensate - * for this. This implies a slight performance decrease. - * - * THIS SHOULD NEVER BE CHANGED! - * - * TODO: Is this true for the latest LX boards still ? - */ - -#define ATA_DMA2_STROBE 4 -#define ATA_DMA2_HOLD 0 -#define ATA_DMA1_STROBE 4 -#define ATA_DMA1_HOLD 1 -#define ATA_DMA0_STROBE 12 -#define ATA_DMA0_HOLD 9 -#define ATA_PIO4_SETUP 1 -#define ATA_PIO4_STROBE 5 -#define ATA_PIO4_HOLD 0 -#define ATA_PIO3_SETUP 1 -#define ATA_PIO3_STROBE 5 -#define ATA_PIO3_HOLD 1 -#define ATA_PIO2_SETUP 1 -#define ATA_PIO2_STROBE 6 -#define ATA_PIO2_HOLD 2 -#define ATA_PIO1_SETUP 2 -#define ATA_PIO1_STROBE 11 -#define ATA_PIO1_HOLD 4 -#define ATA_PIO0_SETUP 4 -#define ATA_PIO0_STROBE 19 -#define ATA_PIO0_HOLD 4 - -static int e100_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq); -static void e100_ideproc (ide_ide_action_t func, struct ata_device *drive, - void *buffer, unsigned int length); - -/* - * good_dma_drives() lists the model names (from "hdparm -i") - * of drives which do not support mword2 DMA but which are - * known to work fine with this interface under Linux. - */ - -const char *good_dma_drives[] = {"Micropolis 2112A", - "CONNER CTMA 4000", - "CONNER CTT8000-A", - NULL}; - -static void tune_e100_ide(struct ata_device *drive, byte pio) -{ - unsigned long flags; - - pio = 4; - /* pio = ide_get_best_pio_mode(drive, pio, 4, NULL); */ - - save_flags(flags); - cli(); - - /* set pio mode! */ - - switch(pio) { - case 0: - *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | - IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | - IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | - IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO0_SETUP ) | - IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO0_STROBE ) | - IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO0_HOLD ) ); - break; - case 1: - *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | - IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | - IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | - IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO1_SETUP ) | - IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO1_STROBE ) | - IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO1_HOLD ) ); - break; - case 2: - *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | - IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | - IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | - IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO2_SETUP ) | - IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO2_STROBE ) | - IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO2_HOLD ) ); - break; - case 3: - *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | - IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | - IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | - IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO3_SETUP ) | - IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO3_STROBE ) | - IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO3_HOLD ) ); - break; - case 4: - *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | - IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | - IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | - IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO4_SETUP ) | - IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO4_STROBE ) | - IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO4_HOLD ) ); - break; - } - restore_flags(flags); -} - -void __init -init_e100_ide (void) -{ - volatile unsigned int dummy; - int h; - - printk("ide: ETRAX 100LX built-in ATA DMA controller\n"); - - /* first initialize the channel interface data */ - - for(h = 0; h < MAX_HWIFS; h++) { - struct ata_channel *hwif = &ide_hwifs[h]; - - hwif->chipset = ide_etrax100; - hwif->tuneproc = &tune_e100_ide; - hwif->udma = &e100_dmaproc; - hwif->ata_read = e100_ide_input_data; - hwif->ata_write = e100_ide_output_data; - hwif->atapi_read = e100_atapi_read; - hwif->atapi_write = e100_atapi_write; - } - /* actually reset and configure the etrax100 ide/ata interface */ - - /* This is mystifying; why is not G27 SET anywhere ? It's just reset here twice. */ - - /* de-assert bus-reset */ -#ifdef CONFIG_ETRAX_IDE_PB7_RESET - port_pb_dir_shadow = port_pb_dir_shadow | - IO_STATE(R_PORT_PB_DIR, dir7, output); - *R_PORT_PB_DIR = port_pb_dir_shadow; - REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 7, 1); -#endif -#ifdef CONFIG_ETRAX_IDE_G27_RESET - *R_PORT_G_DATA = 0; -#endif - - *R_ATA_CTRL_DATA = 0; - *R_ATA_TRANSFER_CNT = 0; - *R_ATA_CONFIG = 0; - - genconfig_shadow = (genconfig_shadow & - ~IO_MASK(R_GEN_CONFIG, dma2) & - ~IO_MASK(R_GEN_CONFIG, dma3) & - ~IO_MASK(R_GEN_CONFIG, ata)) | - ( IO_STATE( R_GEN_CONFIG, dma3, ata ) | - IO_STATE( R_GEN_CONFIG, dma2, ata ) | - IO_STATE( R_GEN_CONFIG, ata, select ) ); - - *R_GEN_CONFIG = genconfig_shadow; - -#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 - - /* wait some */ - udelay(25); - -#ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET - REG_SHADOW_SET(port_cse1_addr, port_cse1_shadow, 16, 1); -#endif -#ifdef CONFIG_ETRAX_IDE_CSP0_8_RESET - REG_SHADOW_SET(port_csp0_addr, port_csp0_shadow, 8, 1); -#endif -#ifdef CONFIG_ETRAX_IDE_G27_RESET - *R_PORT_G_DATA = 0; /* de-assert bus-reset */ -#endif - - /* make a dummy read to set the ata controller in a proper state */ - dummy = *R_ATA_STATUS_DATA; - - *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | - IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | - IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | - IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO4_SETUP ) | - IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO4_STROBE ) | - IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO4_HOLD ) ); - - *R_ATA_CTRL_DATA = ( IO_STATE( R_ATA_CTRL_DATA, rw, read) | - IO_FIELD( R_ATA_CTRL_DATA, addr, 1 ) ); - - while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag*/ - - *R_IRQ_MASK0_SET = ( IO_STATE( R_IRQ_MASK0_SET, ata_irq0, set ) | - IO_STATE( R_IRQ_MASK0_SET, ata_irq1, set ) | - IO_STATE( R_IRQ_MASK0_SET, ata_irq2, set ) | - IO_STATE( R_IRQ_MASK0_SET, ata_irq3, set ) ); - - printk("ide: waiting %d seconds for drives to regain consciousness\n", CONFIG_ETRAX_IDE_DELAY); - - h = jiffies + (CONFIG_ETRAX_IDE_DELAY * HZ); - while(time_before(jiffies, h)) ; - - /* reset the dma channels we will use */ - - RESET_DMA(ATA_TX_DMA_NBR); - RESET_DMA(ATA_RX_DMA_NBR); - WAIT_DMA(ATA_TX_DMA_NBR); - WAIT_DMA(ATA_RX_DMA_NBR); - -} - -static etrax_dma_descr mydescr; - -/* - * The following routines are mainly used by the ATAPI drivers. - * - * These routines will round up any request for an odd number of bytes, - * so if an odd bytecount is specified, be sure that there's at least one - * extra byte allocated for the buffer. - */ -static void -e100_atapi_read(struct ata_device *drive, void *buffer, unsigned int bytecount) -{ - ide_ioreg_t data_reg = IDE_DATA_REG; - - D(printk("atapi_read, dreg 0x%x, buffer 0x%x, count %d\n", - data_reg, buffer, bytecount)); - - if(bytecount & 1) { - printk("warning, odd bytecount in cdrom_in_bytes = %d.\n", bytecount); - bytecount++; /* to round off */ - } - - /* make sure the DMA channel is available */ - RESET_DMA(ATA_RX_DMA_NBR); - WAIT_DMA(ATA_RX_DMA_NBR); - - /* setup DMA descriptor */ - - mydescr.sw_len = bytecount; - mydescr.ctrl = d_eol; - mydescr.buf = virt_to_phys(buffer); - - /* start the dma channel */ - - *R_DMA_CH3_FIRST = virt_to_phys(&mydescr); - *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, start); - - /* initiate a multi word dma read using PIO handshaking */ - - *R_ATA_TRANSFER_CNT = IO_FIELD(R_ATA_TRANSFER_CNT, count, bytecount >> 1); - - *R_ATA_CTRL_DATA = data_reg | - IO_STATE(R_ATA_CTRL_DATA, rw, read) | - IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | - IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | - IO_STATE(R_ATA_CTRL_DATA, multi, on) | - IO_STATE(R_ATA_CTRL_DATA, dma_size, word); - - /* wait for completion */ - - LED_DISK_READ(1); - WAIT_DMA(ATA_RX_DMA_NBR); - LED_DISK_READ(0); - -#if 0 - /* old polled transfer code - * this should be moved into a new function that can do polled - * transfers if DMA is not available - */ - - /* initiate a multi word read */ - - *R_ATA_TRANSFER_CNT = wcount << 1; - - *R_ATA_CTRL_DATA = data_reg | - IO_STATE(R_ATA_CTRL_DATA, rw, read) | - IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | - IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | - IO_STATE(R_ATA_CTRL_DATA, multi, on) | - IO_STATE(R_ATA_CTRL_DATA, dma_size, word); - - /* svinto has a latency until the busy bit actually is set */ - - nop(); nop(); - nop(); nop(); - nop(); nop(); - nop(); nop(); - nop(); nop(); - - /* unit should be busy during multi transfer */ - while((status = *R_ATA_STATUS_DATA) & IO_MASK(R_ATA_STATUS_DATA, busy)) { - while(!(status & IO_MASK(R_ATA_STATUS_DATA, dav))) - status = *R_ATA_STATUS_DATA; - *ptr++ = (unsigned short)(status & 0xffff); - } -#endif -} - -static void -e100_atapi_write(struct ata_device *drive, void *buffer, unsigned int bytecount) -{ - ide_ioreg_t data_reg = IDE_DATA_REG; - - D(printk("atapi_write, dreg 0x%x, buffer 0x%x, count %d\n", - data_reg, buffer, bytecount)); - - if(bytecount & 1) { - printk("odd bytecount %d in atapi_out_bytes!\n", bytecount); - bytecount++; - } - - /* make sure the DMA channel is available */ - RESET_DMA(ATA_TX_DMA_NBR); - WAIT_DMA(ATA_TX_DMA_NBR); - - /* setup DMA descriptor */ - - mydescr.sw_len = bytecount; - mydescr.ctrl = d_eol; - mydescr.buf = virt_to_phys(buffer); - - /* start the dma channel */ - - *R_DMA_CH2_FIRST = virt_to_phys(&mydescr); - *R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, start); - - /* initiate a multi word dma write using PIO handshaking */ - - *R_ATA_TRANSFER_CNT = IO_FIELD(R_ATA_TRANSFER_CNT, count, bytecount >> 1); - - *R_ATA_CTRL_DATA = data_reg | - IO_STATE(R_ATA_CTRL_DATA, rw, write) | - IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | - IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | - IO_STATE(R_ATA_CTRL_DATA, multi, on) | - IO_STATE(R_ATA_CTRL_DATA, dma_size, word); - - /* wait for completion */ - - LED_DISK_WRITE(1); - WAIT_DMA(ATA_TX_DMA_NBR); - LED_DISK_WRITE(0); - -#if 0 - /* old polled write code - see comment in input_bytes */ - - /* wait for busy flag */ - while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); - - /* initiate a multi word write */ - - *R_ATA_TRANSFER_CNT = bytecount >> 1; - - ctrl = data_reg | - IO_STATE(R_ATA_CTRL_DATA, rw, write) | - IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | - IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | - IO_STATE(R_ATA_CTRL_DATA, multi, on) | - IO_STATE(R_ATA_CTRL_DATA, dma_size, word); - - 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. - * 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 - * and then tr_rdy would be 0 forever. This is solved by checking busy - * in the inner loop. - */ - - do { - *R_ATA_CTRL_DATA = ctrl | *ptr++; - while(!(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, tr_rdy)) && - (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy))); - } while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); - - LED_DISK_WRITE(0); -#endif - -} - -/* - * This is used for most PIO data transfers *from* the IDE interface - */ -static void -e100_ide_input_data (struct ata_device *drive, void *buffer, unsigned int wcount) -{ - e100_atapi_read(drive, buffer, wcount << 2); -} - -/* - * This is used for most PIO data transfers *to* the IDE interface - */ -static void -e100_ide_output_data (struct ata_device *drive, void *buffer, unsigned int wcount) -{ - e100_atapi_write(drive, buffer, wcount << 2); -} - -/* 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; - - -/* - * This prepares a dma request. Returns 0 if all went okay, returns 1 - * otherwise. - */ - -static int e100_udma_new_table(struct ata_channel *ch, struct request *rq) -{ - struct buffer_head *bh = rq->bh; - unsigned long size, addr; - unsigned int count = 0; - - ata_tot_size = 0; - - do { - /* - * Determine addr and size of next buffer area. We assume that - * individual virtual buffers are always composed linearly in - * physical memory. For example, we assume that any 8kB buffer - * is always composed of two adjacent physical 4kB pages rather - * than two possibly non-adjacent physical 4kB pages. - */ - if (bh == NULL) { /* paging and tape requests have (rq->bh == NULL) */ - addr = virt_to_phys (rq->buffer); - size = rq->nr_sectors << 9; - } else { - /* group sequential buffers into one large buffer */ - addr = virt_to_phys (bh->b_data); - size = bh->b_size; - while ((bh = bh->b_reqnext) != NULL) { - if ((addr + size) != virt_to_phys (bh->b_data)) - break; - size += bh->b_size; - } - } - - /* did we run out of descriptors? */ - - if(count >= MAX_DMA_DESCRS) { - printk("%s: too few DMA descriptors\n", ch->name); - return 1; - } - - /* however, this case is more difficult - R_ATA_TRANSFER_CNT cannot be more - than 65536 words per transfer, so in that case we need to either - 1) use a DMA interrupt to re-trigger R_ATA_TRANSFER_CNT and continue with - the descriptors, or - 2) simply do the request here, and get dma_intr to only ide_end_request on - those blocks that were actually set-up for transfer. - */ - - if(ata_tot_size + size > 131072) { - printk("too large total ATA DMA request, %d + %d!\n", ata_tot_size, size); - return 1; - } - - /* If size > 65536 it has to be splitted into new descriptors. Since we don't handle - size > 131072 only one split is necessary */ - - if(size > 65536) { - /* ok we want to do IO at addr, size bytes. set up a new descriptor entry */ - ata_descrs[count].sw_len = 0; /* 0 means 65536, this is a 16-bit field */ - ata_descrs[count].ctrl = 0; - ata_descrs[count].buf = addr; - ata_descrs[count].next = virt_to_phys(&ata_descrs[count + 1]); - count++; - ata_tot_size += 65536; - /* size and addr should refere to not handled data */ - size -= 65536; - addr += 65536; - } - /* ok we want to do IO at addr, size bytes. set up a new descriptor entry */ - if(size == 65536) { - ata_descrs[count].sw_len = 0; /* 0 means 65536, this is a 16-bit field */ - } - else { - ata_descrs[count].sw_len = size; - } - ata_descrs[count].ctrl = 0; - ata_descrs[count].buf = addr; - ata_descrs[count].next = virt_to_phys(&ata_descrs[count + 1]); - count++; - ata_tot_size += size; - - } while (bh != NULL); - - if (count) { - /* set the end-of-list flag on the last descriptor */ - ata_descrs[count - 1].ctrl |= d_eol; - /* return and say all is ok */ - return 0; - } - - printk("%s: empty DMA table?\n", ch->name); - return 1; /* let the PIO routines handle this weirdness */ -} - -static int config_drive_for_dma (struct ata_device *drive) -{ - const char **list; - struct hd_driveid *id = drive->id; - - if (id && (id->capability & 1)) { - /* Enable DMA on any drive that supports mword2 DMA */ - if ((id->field_valid & 2) && (id->dma_mword & 0x404) == 0x404) { - drive->using_dma = 1; - return 0; /* DMA enabled */ - } - - /* Consult the list of known "good" drives */ - list = good_dma_drives; - while (*list) { - if (!strcmp(*list++,id->model)) { - drive->using_dma = 1; - return 0; /* DMA enabled */ - } - } - } - return 1; /* DMA not enabled */ -} - -/* - * etrax_dma_intr() is the handler for disk read/write DMA interrupts - */ -static ide_startstop_t etrax_dma_intr(struct ata_device *drive, struct request *rq) -{ - int i, dma_stat; - - LED_DISK_READ(0); - LED_DISK_WRITE(0); - - dma_stat = drive->channel->udma(ide_dma_end, drive, rq); - /* get drive status */ - if (ata_status(drive, DRIVE_READY, drive->bad_wstat | DRQ_STAT)) { - if (!dma_stat) { - for (i = rq->nr_sectors; i > 0;) { - i -= rq->current_nr_sectors; - ide_end_request(drive, rq, 1); - } - return ATA_OP_FINISHED; - } - printk("%s: bad DMA status\n", drive->name); - } - return ata_error(drive, rq, __FUNCTION__); -} - -/* - * e100_dmaproc() 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 - * and then issue the actual read/write DMA/PIO command to the drive. - * - * For ATAPI devices, we just prepare for DMA and return. The caller should - * then issue the packet command to the drive and call us again with - * ide_dma_begin afterwards. - * - * Returns 0 if all went well. - * Returns 1 if DMA read/write could not be started, in which case - * the caller should revert to PIO for the current request. - */ - -static int e100_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq) -{ - 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->type != ATA_DISK) - return 0; - - dma_begin: - - if(reading) { - - RESET_DMA(ATA_RX_DMA_NBR); /* sometimes the DMA channel get stuck so we need to do this */ - WAIT_DMA(ATA_RX_DMA_NBR); - - /* set up the Etrax DMA descriptors */ - - if(e100_udma_new_table(drive->channel, rq)) - return 1; - - if(!atapi) { - /* set the irq handler which will finish the request when DMA is done */ - ata_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL); - - /* issue cmd to drive */ - OUT_BYTE(WIN_READDMA, IDE_COMMAND_REG); - } - - /* begin DMA */ - *R_DMA_CH3_FIRST = virt_to_phys(ata_descrs); - *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, start); - - /* initiate a multi word dma read using DMA handshaking */ - - *R_ATA_TRANSFER_CNT = - IO_FIELD(R_ATA_TRANSFER_CNT, count, ata_tot_size >> 1); - - *R_ATA_CTRL_DATA = - IO_FIELD(R_ATA_CTRL_DATA, data, IDE_DATA_REG) | - IO_STATE(R_ATA_CTRL_DATA, rw, read) | - IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | - IO_STATE(R_ATA_CTRL_DATA, handsh, dma) | - IO_STATE(R_ATA_CTRL_DATA, multi, on) | - IO_STATE(R_ATA_CTRL_DATA, dma_size, word); - - LED_DISK_READ(1); - - D(printk("dma read of %d bytes.\n", ata_tot_size)); - - } else { - /* writing */ - - RESET_DMA(ATA_TX_DMA_NBR); /* sometimes the DMA channel get stuck so we need to do this */ - WAIT_DMA(ATA_TX_DMA_NBR); - - /* set up the Etrax DMA descriptors */ - - if(e100_udma_new_table(drive->channel, rq)) - return 1; - - if(!atapi) { - /* set the irq handler which will finish the request when DMA is done */ - ata_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL); - - /* issue cmd to drive */ - OUT_BYTE(WIN_WRITEDMA, IDE_COMMAND_REG); - } - - /* begin DMA */ - *R_DMA_CH2_FIRST = virt_to_phys(ata_descrs); - *R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, start); - - /* initiate a multi word dma write using DMA handshaking */ - *R_ATA_TRANSFER_CNT = - IO_FIELD(R_ATA_TRANSFER_CNT, count, ata_tot_size >> 1); - - *R_ATA_CTRL_DATA = - IO_FIELD(R_ATA_CTRL_DATA, data, IDE_DATA_REG) | - IO_STATE(R_ATA_CTRL_DATA, rw, write) | - IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | - IO_STATE(R_ATA_CTRL_DATA, handsh, dma) | - IO_STATE(R_ATA_CTRL_DATA, multi, on) | - IO_STATE(R_ATA_CTRL_DATA, dma_size, word); - - LED_DISK_WRITE(1); - - 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 */ - -/* Dear maintainer of this architecture please note that it would be a little - * more clever :-) to put this up into some header as static inline, so the - * spurious code below would just vanish. - * - * --- Marcin Dalecki - */ - -void ide_release_dma(struct ata_channel *ch) -{ - /* empty */ -} diff --git a/arch/cris/drivers/lpslave/Makefile b/arch/cris/drivers/lpslave/Makefile deleted file mode 100644 index 6d7982db01e8..000000000000 --- a/arch/cris/drivers/lpslave/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# -# Makefile for parallel port slave drivers -# - -obj-y = e100lpslavenet.o e100lpslave_code.o - -e100lpslave_code.o: e100lpslave.o e100lpslaveld - $(CROSS_COMPILE)ld -qmagic -Te100lpslaveld e100lpslave.o -o e100lpslave - $(CROSS_COMPILE)objcopy -O binary --remove-section=.data --remove-section=.bss e100lpslave e100lpslave.text - $(CROSS_COMPILE)objcopy -O binary --remove-section=.text --remove-section=.bss e100lpslave e100lpslave.data - cat e100lpslave.text e100lpslave.data |\ - ./bintocarr.pl e100lpslaveprog |\ - $(CC) $(CFLAGS) -pipe -o e100lpslave_code.o -c -x c - - ls -l e100lpslave.text e100lpslave.data - rm -f e100lpslave e100lpslave.text e100lpslave.data diff --git a/arch/cris/drivers/lpslave/bintocarr.pl b/arch/cris/drivers/lpslave/bintocarr.pl deleted file mode 100644 index c03dd8b718b7..000000000000 --- a/arch/cris/drivers/lpslave/bintocarr.pl +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/perl -w -# $Id: bintocarr.pl,v 1.1.1.1 2001/12/17 13:59:27 bjornw 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 - -$symbol = shift @ARGV; - -print "#include <linux/init.h>\n\n"; -#print "unsigned char $symbol", "[] __initdata = {\n"; -print "unsigned char $symbol", "[] = {\n"; - -my $char; - -$bcount = 0; - -while(read STDIN, $char, 1) { - printf("0x%x, ", ord($char)); - $bcount++; - if(!($bcount % 16)) { - print "\n"; - } -} - -print "\n};\n"; - -$lensymb = ("_" . ($symbol . "_length")); - -print "__asm__(\"\\t.globl $lensymb\\n$lensymb = $bcount\\n\");\n"; - diff --git a/arch/cris/drivers/lpslave/e100lpslave.README b/arch/cris/drivers/lpslave/e100lpslave.README deleted file mode 100644 index eb580a9d2dac..000000000000 --- a/arch/cris/drivers/lpslave/e100lpslave.README +++ /dev/null @@ -1,54 +0,0 @@ -***** MODIFICATIONS -To use a 5600 as a slave device it has to somewhat modified. -This has to be done on a 5600 (art no 16144 R2) to automatically set it to parallel port boot mode: - -1) -Close to the LPT1 connector there are two resistors, between the "Pulse" inductor and -the LS245. The one closest to the LS245 is a 4k7 pull up (R105). Remove it. - -2) -Between the other "Pulse" inductor and Etrax there is a black 5-pin inverter -(D15). Short connectors 2 and 3 with a solder blob. - - -***** PINOUT -To use this driver use cables connected like this: -DSUB25-Male DSUB25Male - -1 10 -2-9 2-9 -10 1 -11 14 -12 18 -13 NC -14 11 -15 NC -16 NC -17 NC -18 12 -19 NC -20-25 20-25 - -Thus the cables are symmetrical with most cables straight through, -some crossed (1-10, 11-14 and 12-18) -and some Not connected (NC 13,15,16,17 and 19). - - -******* Only for reference -To ease the use of flat-cable connectors, here are the notes of wich wires to cross and cut with pin 1 being cable 1: -Cross: -1 19 -2 21 -10 23 -Cut: -4 -6 -8 -12 -25 - -jonas.dellenvall@axis.com - - - - diff --git a/arch/cris/drivers/lpslave/e100lpslave.S b/arch/cris/drivers/lpslave/e100lpslave.S deleted file mode 100644 index 44efe0771fc2..000000000000 --- a/arch/cris/drivers/lpslave/e100lpslave.S +++ /dev/null @@ -1,429 +0,0 @@ - ;; $Id: e100lpslave.S,v 1.1.1.1 2001/12/17 13:59:27 bjornw 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) - ;; - -#include <linux/config.h> -#if 0 -#define ASSEMBLER_MACROS_ONLY -#endif -#include <asm/sv_addr_ag.h> - -#define BUFSIZE 0x600 - - ;; R_IRQ_READ2 - -#define DMA1EOPBIT 3 -#define DMA0EOPBIT 1 -#define DMA3EOPBIT 7 -#define DMA4DESCBIT 8 - - ;; R_IRQ_READ0 - -#define PAR0ECPCMDBIT 11 - - ;; get host CMDs - -#include "e100lpslave.h" - -start: - ;; disable interrupts. we are not going to use them at all. - - di - - ;; setup DMA connections and port configuration - - movu.w 0x84, r0 ; DMA2/3/4/5 to par ports - move.d r0, [R_GEN_CONFIG] - - ;; setup port PA dirs and turn on the LED to show were alive - - movu.w 0x0cfb, r0 ; PA2-PA3 out, PA2 inactive - move.d r0, [R_PORT_PA_SET] - - ;; enable MDIO output pin - moveq IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable), r0 - move.d r0, [R_NETWORK_MGM_CTRL] - - ;; accept broadcast frames, and enable station address 0 - moveq IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) | \ - IO_STATE(R_NETWORK_REC_CONFIG, ma0, enable), r0 - move.d r0, [R_NETWORK_REC_CONFIG] - - ;; use MII CLK mode, and enable the controller - moveq IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) | \ - IO_STATE(R_NETWORK_GEN_CONFIG, enable, on), r0 - move.d r0, [R_NETWORK_GEN_CONFIG] - - move.d IO_STATE(R_PAR0_CONFIG, ioe, noninv) | \ - IO_STATE(R_PAR0_CONFIG, iseli, noninv) | \ - IO_STATE(R_PAR0_CONFIG, iautofd, noninv) | \ - IO_STATE(R_PAR0_CONFIG, istrb, noninv) | \ - IO_STATE(R_PAR0_CONFIG, iinit, noninv) | \ - IO_STATE(R_PAR0_CONFIG, iperr, noninv) | \ - IO_STATE(R_PAR0_CONFIG, iack, noninv) | \ - IO_STATE(R_PAR0_CONFIG, ibusy, noninv) | \ - IO_STATE(R_PAR0_CONFIG, ifault, noninv) | \ - IO_STATE(R_PAR0_CONFIG, isel, noninv) | \ - IO_STATE(R_PAR0_CONFIG, dma, enable) | \ - IO_STATE(R_PAR0_CONFIG, rle_in, disable) | \ - IO_STATE(R_PAR0_CONFIG, rle_out, disable) | \ - IO_STATE(R_PAR0_CONFIG, enable, on) | \ - IO_STATE(R_PAR0_CONFIG, force, on) | \ - IO_STATE(R_PAR0_CONFIG, mode, ecp_rev), r0 ; Reverse ECP - PAR0 is RX - - move.d r0, [R_PAR0_CONFIG] - - move.d IO_STATE(R_PAR1_CONFIG, ioe, noninv) | \ - IO_STATE(R_PAR1_CONFIG, iseli, noninv) | \ - IO_STATE(R_PAR1_CONFIG, iautofd, noninv) | \ - IO_STATE(R_PAR1_CONFIG, istrb, noninv) | \ - IO_STATE(R_PAR1_CONFIG, iinit, noninv) | \ - IO_STATE(R_PAR1_CONFIG, iperr, inv) | \ - IO_STATE(R_PAR1_CONFIG, iack, noninv) | \ - IO_STATE(R_PAR1_CONFIG, ibusy, noninv) | \ - IO_STATE(R_PAR1_CONFIG, ifault, noninv) | \ - IO_STATE(R_PAR1_CONFIG, isel, noninv) | \ - IO_STATE(R_PAR1_CONFIG, dma, enable) | \ - IO_STATE(R_PAR1_CONFIG, rle_in, disable) | \ - IO_STATE(R_PAR1_CONFIG, rle_out, disable) | \ - IO_STATE(R_PAR1_CONFIG, enable, on) | \ - IO_STATE(R_PAR1_CONFIG, force, on) | \ - IO_STATE(R_PAR1_CONFIG, mode, ecp_fwd), r0 ; Forward ECP - PAR1 is TX - - move.d r0, [R_PAR1_CONFIG] - - moveq IO_FIELD(R_PAR1_DELAY, setup, 0), r0 ; setup time of value * 160 + 20 == 20 ns - move.d r0, [R_PAR1_DELAY] - - ;; we got four descriptors, that can be active at the same time: - ;; 1) from network - ;; 2) to parport - ;; 3) from parport - ;; 4) to network - ;; - ;; we got four buffers, each can hold a max packet (we use 1536 bytes) - ;; buffers 1 and 2 are used from network to parport, while - ;; buffers 3 and 4 are used from parport to network. - ;; - ;; a double buffering scheme is used, so that new data can be read - ;; into a buffer pair while the last data is written out from the - ;; last buffer. if the read buffer is done before the write buffer, - ;; the reading will halt until the writing is done, at which point - ;; writing starts from the newly read and reading can start with - ;; the newly written. - ;; - - move.d R_DMA_CH0_FIRST, r1 ; we use this as base for subsequent DMA ops - moveq IO_STATE(R_DMA_CH1_CMD, cmd, start), r6 - move.d FN1desc, r7 - move.d R_IRQ_READ0, r9 - - ;; start receiving from network - - jsr startdmaFPTN - jsr startdmaFNTP - - - - ;; ------------------- MAIN LOOP - - ;; IRQ bits: parport rcv is par0_ecp_cmd, then dma3_eop - ;; network rcv is dma1_eop - ;; parport tx is dma4_desc - ;; network tx is dma0_eop - -mainloop: - - ;; ------- first handle the parport -> network link - - ;; check if we got something from the parport - - move.d [r9], r0 ; r0 <- *R_IRQ_READ0 - btstq PAR0ECPCMDBIT, r0 - bpl noparecp - nop - - ;; ack it by reading PAR0_STATUS_DATA - - move.d [R_PAR0_STATUS_DATA], r0 - - ;; trigger EOP on DMA3 (par0 incoming channel) - - moveq IO_STATE(R_SET_EOP, ch3_eop, set), r0 - move.d r0, [R_SET_EOP] - -noparecp: - - ;; if we simultaneously have parport rx EOP and - ;; network TX eop, we can swap buffers and start a new RX/TX - - move.d [r9 + (R_IRQ_READ2 - R_IRQ_READ0)], r0 - btstq DMA3EOPBIT, r0 ; check parport rx - bpl noswap1 - btstq DMA0EOPBIT, r0 ; check network tx - bpl noswap1 - nop - - ;; prepare to swap buffer ptrs (FN3b <-> TN4b) - - move.d [r4 = r7 + 56], r0; FP3b - move.d [r3 = r7 + 72], r2; TN4b - - ;; but first check if this was a Host Command Packet - - move.d [r0], r5 ; r5 <- first 4 bytes in PAR-received packet - bne handle_command ; if non-zero, it was a host command - addq 4, r0 ; skip command (in delay slot - handle_command requires this) - move.d r0, [r3] ; write to To Network descriptor - subq 4, r2 ; undo the skipping done last swap - move.d r2, [r4] ; write to From Parport descriptor - - ;; clear the interrupts - - moveq IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do), r0 - move.b r0, [r1 + (R_DMA_CH0_CLR_INTR - R_DMA_CH0_FIRST)] - move.b r0, [r1 + (R_DMA_CH3_CLR_INTR - R_DMA_CH0_FIRST)] - - ;; copy received length to outgoing network length - - move.w [r7 + 60], r0 ; FPhlen - subq 4, r0 ; skip command - move.w r0, [r7 + 64] ; TN4desc - - ;; restart DMAs - - jsr startdmaFPTN - -#ifdef CONFIG_ETRAX_ETHERNET_LPSLAVE_HAS_LEDS -#if defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK) - ;; Turn off the LED signaling an outgoing network packet - movu.b [LEDOff], r0 -#elif defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY) - ;; Light the LED signaling an outgoing network packet - movu.b [LEDAmber], r0 -#else -#error "Define either CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK or CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY" -#endif - move.b r0, [R_PORT_PA_DATA] - move.d 0x00011000, r0 - move.d r0,[LEDCount] -#endif - -noswap1: - ;; ----- now check the network -> parport link - - - ;; if we simultaneously have network rx EOP and - ;; parport TX desc, we can swap buffers and start a new RX/TX - - move.d [r9 + (R_IRQ_READ2 - R_IRQ_READ0)], r0 - btstq DMA1EOPBIT, r0 ; check network rx - bpl noswap2 - btstq DMA4DESCBIT, r0 ; check parport tx - bpl noswap2 - nop - - ;; prepare to swap buffer ptrs (FP1b <-> TP2b) - - move.d [r4 = r7 + 8], r0; FN1b - move.d [r3 = r7 + 24], r2; TP2b - move.d r0, [r3] ; write to To Parport descriptor - move.d r2, [r4] ; write to From Network descriptor - - ;; clear the interrupts - - moveq IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do) | \ - IO_STATE(R_DMA_CH1_CLR_INTR, clr_descr, do), r0 - move.b r0, [r1 + (R_DMA_CH1_CLR_INTR - R_DMA_CH0_FIRST)] - move.b r0, [r1 + (R_DMA_CH4_CLR_INTR - R_DMA_CH0_FIRST)] - - ;; copy received network length to outgoing parport length - - move.w [r7 + 12], r0 ; FNhlen - move.w r0, [r7 + 16] ; TP2desc - - ;; restart DMAs - - jsr startdmaFNTP -#if 0 -#ifdef CONFIG_ETRAX_ETHERNET_LPSLAVE_HAS_LEDS - ;; Light the LED signaling an incoming networkpacket - movu.b 0xFB, r0 - move.b r0, [R_PORT_PA_DATA] - move.d 0x00010000, r0 - move.d r0,[LEDCount] -#endif -#endif - -noswap2: -#ifdef CONFIG_ETRAX_ETHERNET_LPSLAVE_HAS_LEDS - - ;; Count down LED counter, and turn off the network LED if required - move.d [LEDCount], r0 - beq mainloop - nop - - subq 1, r0 - move.d r0, [LEDCount] - bne mainloop - nop - -#if defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK) - ;; Light the network LED , and start over the main loop - movu.b [LEDAmber], r0 -#elif defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY) - ;; Turn off the network LED, and start over the main loop - movu.b [LEDOff], r0 -#else -#error "Define either CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK or CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY" -#endif - move.b r0, [R_PORT_PA_DATA] -#endif - - ba mainloop - nop - - ;; --- some useful subroutines. - -handle_command: - ;; handle command. we also need to clear the PAR0 RX EOP IRQ, and - ;; restart the PAR0 dma. command is in R5, packet after cmd is in R0 - - moveq IO_STATE(R_DMA_CH3_CLR_INTR, clr_eop, do), r2 - move.b r2, [r1 + (R_DMA_CH3_CLR_INTR - R_DMA_CH0_FIRST)] - - cmpq HOST_CMD_SETMAC, r5 - bne no_setmac - nop - - ;; copy station address (6 bytes) from packet to hardware - - move.d [r0+], r2 - move.d R_NETWORK_SA_0, r3 - move.d r2, [r3] - move.w [r0], r2 - move.w r2, [r3 + 4] - -no_setmac: - move noswap1, SRP - ba startdmaFP - nop - - ;; start DMAs, from parport and to network - -startdmaFPTN: - - ;; start transmitting to the network (CH0) - - move.d TN4desc, r8 - move.d r8, [r1] ; TN4desc -> FIRST0 - move.b r6, [r1 + (R_DMA_CH0_CMD - R_DMA_CH0_FIRST)] ; start -> CMD0 - -startdmaFP: - - ;; start receiving from parport (CH3) - - move.d FP3desc, r8 - move.d r8, [r1 + (R_DMA_CH3_FIRST - R_DMA_CH0_FIRST)] ; FP3desc -> FIRST3 - move.b r6, [r1 + (R_DMA_CH3_CMD - R_DMA_CH0_FIRST)] ; start -> CMD3 - - ret - nop - - ;; start DMAs, from network and to parport - -startdmaFNTP: - - ;; start transmitting to the parport (CH4) - - move.d TP2desc, r8 - move.d r8, [r1 + (R_DMA_CH4_FIRST - R_DMA_CH0_FIRST)] ; TP2desc -> FIRST4 - move.b r6, [r1 + (R_DMA_CH4_CMD - R_DMA_CH0_FIRST)] ; start -> CMD4 - - ;; start receiving from network (CH1) (r7 already contains FN1desc) - - move.d r7, [r1 + (R_DMA_CH1_FIRST - R_DMA_CH0_FIRST)] ; FN1desc -> FIRST1 - move.b r6, [r1 + (R_DMA_CH1_CMD - R_DMA_CH0_FIRST)] ; start -> CMD1 - - ret - nop - - ;; --- DMA descriptors - each descriptor is 4 longwords (16 bytes) - ;; DONT MOVE THESE AROUND. Due to the as/ld "hole-in-the-head", - ;; we cant write stuff like (TP2b - TP2desc) but the offsets - ;; have to be hardcoded. - - .data - - ;; 0 from network -FN1desc: - .word BUFSIZE ; sw_len - .word 0x0001 ; ctrl, d_eol is only flag we need - .dword 0 ; next -FN1b: .dword buffers ; buffer 1 8 - .word 0 ; hw_len - .word 0 ; status - - ;; 16 to parport -TP2desc: - .word 2 ; sw_len, filled in by code - .word 0x0004 ; ctrl, d_wait because ecp cmd in next - .dword TP2desc2 ; next -TP2b: .dword buffers + BUFSIZE ; buffer 2 24 - .word 0 ; hw_len - .word 0 ; status - - ;; 32 to parport second descriptor, for the ECP command -TP2desc2: - .word 0x0001 ; sw_len, 1 byte (ecp command) - .word 0x0019 ; ctrl, d_ecp | d_eol | d_int - .dword 0 ; next - .dword TP2desc2 ; buffer, dont care - .word 0 ; hw_len - .word 0 ; status - - ;; 48 from parport -FP3desc: - .word BUFSIZE ; sw_len - .word 0x0001 ; ctrl, d_eol is only flag we need - .dword 0 ; next -FP3b: .dword buffers + BUFSIZE * 2 ; 56 buffer 3 -FPhlen: .word 0 ; 60 hw_len - .word 0 ; status - - ;; 64 to network -TN4desc: - .word 2 ; sw_len, filled in by code - .word 0x0007 ; ctrl, d_eop | d_eol | d_wait - .dword 0 ; next -TN4b: .dword buffers + BUFSIZE * 3 + 4 ; 72 buffer 4 (the +4 is to offset the anti-skipping) - .word 0 ; hw_len - .word 0 ; status - -#ifdef CONFIG_ETRAX_ETHERNET_LPSLAVE_HAS_LEDS -LEDCount: - .dword 0 -LEDOff: - .word 0xff -LEDGreen: - .word 0xfb -LEDRed: - .word 0xf7 -LEDAmber: - .word 0xf3 -LED: - .word 0xf7 -#endif - - ;; after the prog we put the buffers. not in the asm program, we just use - ;; the address generated - -buffers: - - ;; END diff --git a/arch/cris/drivers/lpslave/e100lpslave.h b/arch/cris/drivers/lpslave/e100lpslave.h deleted file mode 100644 index 7c9cfd91e365..000000000000 --- a/arch/cris/drivers/lpslave/e100lpslave.h +++ /dev/null @@ -1,2 +0,0 @@ -#define HOST_CMD_SENDPACK 0 -#define HOST_CMD_SETMAC 1 diff --git a/arch/cris/drivers/lpslave/e100lpslaveld b/arch/cris/drivers/lpslave/e100lpslaveld deleted file mode 100644 index 4d51ff40ef65..000000000000 --- a/arch/cris/drivers/lpslave/e100lpslaveld +++ /dev/null @@ -1,22 +0,0 @@ -MEMORY - { - cache : ORIGIN = 0x380000f0, - LENGTH = 784 - } - -SECTIONS -{ - .text : - { - *(.text) - } > cache - .data : - { - *(.data) - *(COMMON) - } > cache - .bss : - { - *(.bss) - } > cache -} diff --git a/arch/cris/drivers/lpslave/e100lpslavenet.c b/arch/cris/drivers/lpslave/e100lpslavenet.c deleted file mode 100644 index 29e98e46c321..000000000000 --- a/arch/cris/drivers/lpslave/e100lpslavenet.c +++ /dev/null @@ -1,1012 +0,0 @@ -/* $Id: e100lpslavenet.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ - * - * e100lpslavenet.c: A network driver for the ETRAX 100LX slave controller. - * - * Copyright (c) 1998-2001 Axis Communications AB. - * - * The outline of this driver comes from skeleton.c. - * - * $Log: e100lpslavenet.c,v $ - * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw - * Import of Linux 2.5.1 - * - * Revision 1.4 2001/06/21 16:55:26 olof - * Minimized par port setup time to gain bandwidth - * - * Revision 1.3 2001/06/21 15:49:02 olof - * Removed setting of default MAC address - * - * Revision 1.2 2001/06/11 15:39:52 olof - * Clean up and sync with ethernet.c rev 1.16. Increased reset time of slave. - * - * Revision 1.1 2001/06/06 08:56:26 olof - * Added support for slave Etrax defined by CONFIG_ETRAX_ETHERNET_LPSLAVE - * - */ - -#include <linux/config.h> - -#include <linux/module.h> - -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/delay.h> -#include <linux/types.h> -#include <linux/fcntl.h> -#include <linux/interrupt.h> -#include <linux/ptrace.h> -#include <linux/ioport.h> -#include <linux/in.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/spinlock.h> -#include <linux/errno.h> -#include <linux/init.h> - -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> - -#include <asm/svinto.h> /* DMA and register descriptions */ -#include <asm/io.h> /* LED_* I/O functions */ -#include <asm/irq.h> -#include <asm/dma.h> -#include <asm/system.h> -#include <asm/bitops.h> - -#include "e100lpslave.h" - -/* #define ETHDEBUG */ -#define D(x) - -/* - * The name of the card. Is used for messages and in the requests for - * io regions, irqs and dma channels - */ - -static const char* cardname = "Etrax 100LX ethernet slave controller"; - -/* A default ethernet address. Highlevel SW will set the real one later */ - -static struct sockaddr default_mac = { - 0, - { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 } -}; - -/* Information that need to be kept for each board. */ -struct net_local { - struct net_device_stats stats; - - /* Tx control lock. This protects the transmit buffer ring - * state along with the "tx full" state of the driver. This - * means all netif_queue flow control actions are protected - * by this lock as well. - */ - spinlock_t lock; -}; - -/* Dma descriptors etc. */ - -#define RX_BUF_SIZE 32768 -#define ETHER_HEAD_LEN 14 - -#define PAR0_ECP_IRQ_NBR 4 - -#define RX_DESC_BUF_SIZE 256 -#define NBR_OF_RX_DESC (RX_BUF_SIZE / \ - RX_DESC_BUF_SIZE) - -/* Size of slave etrax boot image */ -#define ETRAX_PAR_BOOT_LENGTH 784 - -static etrax_dma_descr *myNextRxDesc; /* Points to the next descriptor to - to be processed */ -static etrax_dma_descr *myLastRxDesc; /* The last processed descriptor */ -static etrax_dma_descr *myPrevRxDesc; /* The descriptor right before myNextRxDesc */ - -static unsigned char RxBuf[RX_BUF_SIZE]; - -static etrax_dma_descr RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned(4))); -static etrax_dma_descr TxDescList[3] __attribute__ ((aligned(4))); - /* host command, data, bogus ECP command */ - -static struct sk_buff *tx_skb; - -/* Index to functions, as function prototypes. */ - -static int etrax_ethernet_lpslave_init(struct net_device *dev); - -static int e100_open(struct net_device *dev); -static int e100_set_mac_address(struct net_device *dev, void *addr); -static int e100_send_packet(struct sk_buff *skb, struct net_device *dev); -static void e100rx_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static void e100tx_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static void ecp_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static void e100_rx(struct net_device *dev); -static int e100_close(struct net_device *dev); -static struct net_device_stats *e100_get_stats(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static void e100_hardware_send_packet(unsigned long hostcmd, char *buf, int length); -static void update_rx_stats(struct net_device_stats *); -static void update_tx_stats(struct net_device_stats *); -static void e100_reset_transceiver(void); - -static void boot_slave(unsigned char *code); - -#ifdef ETHDEBUG -static void dump_parport_status(void); -#endif - -#define tx_done(dev) (*R_DMA_CH0_CMD == 0) - -static unsigned long host_command; -extern unsigned char e100lpslaveprog; - -/* - * This driver uses PAR0 to recevice data from slave ETRAX and PAR1 to boot - * and send data to slave ETRAX. - * Used ETRAX100 DMAchannels with corresponding IRQ: - * PAR0 RX : DMA3 - IRQ 19 - * PAR1 TX : DMA4 - IRQ 20 - * IRQ 4 is used to detect ECP commands from slave ETRAX - * - * NOTE! PAR0 and PAR1 shares DMA and IRQ numbers with SER2 and SER3 - */ - - -/* - * Check for a network adaptor of this type, and return '0' if one exists. - * If dev->base_addr == 0, probe all likely locations. - * If dev->base_addr == 1, always return failure. - * If dev->base_addr == 2, allocate space for the device and return success - * (detachable devices only). - */ -static int __init -etrax_ethernet_lpslave_init(void) -{ - struct net_device *dev; - int i, err; - int anOffset = 0; - - printk("Etrax/100 lpslave ethernet driver v0.3, (c) 1999 Axis Communications AB\n"); - - dev = alloc_etherdev(sizeof(struct net_lock)); - if (!dev) - return -ENOMEM; - - dev->base_addr = 2; - - /* now setup our etrax specific stuff */ - - dev->irq = DMA3_RX_IRQ_NBR; /* we really use DMATX as well... */ - dev->dma = PAR0_RX_DMA_NBR; - - /* fill in our handlers so the network layer can talk to us in the future */ - - dev->open = e100_open; - dev->hard_start_xmit = e100_send_packet; - dev->stop = e100_close; - dev->get_stats = e100_get_stats; - dev->set_multicast_list = set_multicast_list; - dev->set_mac_address = e100_set_mac_address; - - /* Initialise the list of Etrax DMA-descriptors */ - - /* Initialise receive descriptors */ - - for(i = 0; i < (NBR_OF_RX_DESC - 1); i++) { - RxDescList[i].ctrl = 0; - RxDescList[i].sw_len = RX_DESC_BUF_SIZE; - RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]); - RxDescList[i].buf = virt_to_phys(RxBuf + anOffset); - RxDescList[i].status = 0; - RxDescList[i].hw_len = 0; - anOffset += RX_DESC_BUF_SIZE; - } - - RxDescList[i].ctrl = d_eol; - RxDescList[i].sw_len = RX_DESC_BUF_SIZE; - RxDescList[i].next = virt_to_phys(&RxDescList[0]); - RxDescList[i].buf = virt_to_phys(RxBuf + anOffset); - RxDescList[i].status = 0; - RxDescList[i].hw_len = 0; - - /* Initialise initial pointers */ - - myNextRxDesc = &RxDescList[0]; - myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; - myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; - - /* setup some TX descriptor data */ - - TxDescList[0].sw_len = 4; - TxDescList[0].ctrl = 0; - TxDescList[0].buf = virt_to_phys(&host_command); - TxDescList[0].next = virt_to_phys(&TxDescList[1]); - - err = register_netdev(dev); - if (err) - kfree(dev); - - return err; -} - -/* set MAC address of the interface. called from the core after a - * SIOCSIFADDR ioctl, and from the bootup above. - */ - -static int -e100_set_mac_address(struct net_device *dev, void *p) -{ - struct sockaddr *addr = p; - int i; - - /* remember it */ - - memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - - /* Write it to the hardware. - * Note the way the address is wrapped: - * *R_NETWORK_SA_0 = a0_0 | (a0_1 << 8) | (a0_2 << 16) | (a0_3 << 24); - * *R_NETWORK_SA_1 = a0_4 | (a0_5 << 8); - */ - - tx_skb = 0; - e100_hardware_send_packet(HOST_CMD_SETMAC, dev->dev_addr, 6); - - /* show it in the log as well */ - - printk("%s: changed MAC to ", dev->name); - - for (i = 0; i < 5; i++) - printk("%02X:", dev->dev_addr[i]); - - printk("%02X\n", dev->dev_addr[i]); - - return 0; -} - -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ - -static int -e100_open(struct net_device *dev) -{ - unsigned long flags; - - /* configure the PAR0 (RX) and PAR1 (TX) ports - * - * perror is nAckReverse, which must be 1 at the TX side, - * and 0 at the RX side - * - * select is XFlag, which must be 1 at both sides - */ -#ifdef ETHDEBUG - printk("Setting up PAR ports\n"); -#endif - *R_PAR0_CONFIG = - /* We do not have an external buffer, don't care */ - IO_STATE(R_PAR0_CONFIG, ioe, noninv) | - /* Not connected, don't care */ - IO_STATE(R_PAR0_CONFIG, iseli, noninv) | - /* iautofd is not inverted, noninv */ - IO_STATE(R_PAR0_CONFIG, iautofd, noninv) | - /* Not used in reverse direction, don't care */ - IO_STATE(R_PAR0_CONFIG, istrb, noninv) | - /* Not connected, don't care */ - IO_STATE(R_PAR0_CONFIG, iinit, noninv) | - /* perror is GND and reverse wants 0, noninv */ - IO_STATE(R_PAR0_CONFIG, iperr, noninv) | - /* ack is not inverted, noninv */ - IO_STATE(R_PAR0_CONFIG, iack, noninv) | - /* busy is not inverted, noninv */ - IO_STATE(R_PAR0_CONFIG, ibusy, noninv) | - /* fault is not inverted, noninv */ - IO_STATE(R_PAR0_CONFIG, ifault, noninv) | - /* select is Vcc and we want 1, noninv */ - IO_STATE(R_PAR0_CONFIG, isel, noninv) | - /* We will run dma, enable */ - IO_STATE(R_PAR0_CONFIG, dma, enable) | - /* No run length encoding, disable */ - IO_STATE(R_PAR0_CONFIG, rle_in, disable) | - /* No run length encoding, disable */ - IO_STATE(R_PAR0_CONFIG, rle_out, disable) | - /* Enable parallel port */ - IO_STATE(R_PAR0_CONFIG, enable, on) | - /* Force mode regardless of pin status */ - IO_STATE(R_PAR0_CONFIG, force, on) | - /* We want ECP forward mode since PAR0 is RX */ - IO_STATE(R_PAR0_CONFIG, mode, ecp_rev); - - *R_PAR1_CONFIG = - /* We do not have an external buffer, don't care */ - IO_STATE(R_PAR1_CONFIG, ioe, noninv) | - - /* Not connected, don't care */ - IO_STATE(R_PAR1_CONFIG, iseli, noninv) | - - /* HostAck must indicate data cycle, noninv */ - IO_STATE(R_PAR1_CONFIG, iautofd, noninv) | - - /* HostClk has no external inverter, noninv */ - IO_STATE(R_PAR1_CONFIG, istrb, noninv) | - - /* Not connected, don't care */ - IO_STATE(R_PAR1_CONFIG, iinit, noninv) | - - /* nAckReverse must be 1 in forward mode but is grounded, inv */ - IO_STATE(R_PAR1_CONFIG, iperr, inv) | - - /* PeriphClk must be 1 in forward mode, noninv */ - IO_STATE(R_PAR1_CONFIG, iack, noninv) | - - /* PeriphAck has no external inverter, noninv */ - IO_STATE(R_PAR1_CONFIG, ibusy, noninv) | - - /* nPerihpRequest has no external inverter, noniv */ - IO_STATE(R_PAR1_CONFIG, ifault, noninv) | - - /* Select is VCC and we want 1, noninv */ - IO_STATE(R_PAR1_CONFIG, isel, noninv) | - - /* No EPP mode, disable */ - IO_STATE(R_PAR1_CONFIG, ext_mode, disable) | - - /* We will run dma, enable */ - IO_STATE(R_PAR1_CONFIG, dma, enable) | - - /* No run length encoding, disable */ - IO_STATE(R_PAR1_CONFIG, rle_in, disable) | - - /* No run length encoding, disable */ - IO_STATE(R_PAR1_CONFIG, rle_out, disable) | - - /* Enable parallel port */ - IO_STATE(R_PAR1_CONFIG, enable, on) | - - /* Force mode regardless of pin status */ - IO_STATE(R_PAR1_CONFIG, force, on) | - - /* We want ECP forward mode since PAR1 is TX */ - IO_STATE(R_PAR1_CONFIG, mode, ecp_fwd); - - /* Setup time of value * 160 + 20 ns == 20 ns below */ - *R_PAR1_DELAY = IO_FIELD(R_PAR1_DELAY, setup, 0); - - *R_PAR1_CTRL = 0; - - while ((((*R_PAR1_STATUS)&0xE000) >> 13) != 5); /* Wait for ECP_FWD mode */ -#ifdef ETHDEBUG - dump_parport_status(); -#endif - - /* make sure ECP irq is acked when we enable it below */ - - (void)*R_PAR0_STATUS_DATA; - (void)*R_PAR1_STATUS_DATA; - - /* Reset and wait for the DMA channels */ - - RESET_DMA(4); /* PAR1_TX_DMA_NBR */ - RESET_DMA(3); /* PAR0_RX_DMA_NBR */ - WAIT_DMA(4); - WAIT_DMA(3); - - /* boot the slave Etrax, by sending code on PAR1. - * do this before we start up the IRQ handlers and stuff, - * beacuse we simply poll for completion in boot_slave. - */ - - boot_slave(&e100lpslaveprog); - - /* allocate the irq corresponding to the receiving DMA */ - - if (request_irq(DMA3_RX_IRQ_NBR, e100rx_interrupt, 0, - cardname, (void *)dev)) { - printk("Failed to allocate DMA3_RX_IRQ_NBR\n"); - goto grace_exit; - } - - /* allocate the irq corresponding to the transmitting DMA */ - - if (request_irq(DMA4_TX_IRQ_NBR, e100tx_interrupt, 0, - cardname, (void *)dev)) { - printk("Failed to allocate DMA4_TX_IRQ_NBR\n"); - goto grace_exit; - } - - /* allocate the irq used for detecting ECP commands on the RX port (PAR0) */ - - if (request_irq(PAR0_ECP_IRQ_NBR, ecp_interrupt, 0, - cardname, (void *)dev)) { - printk("Failed to allocate PAR0_ECP_IRQ_NBR\n"); - grace_exit: - free_irq(PAR0_ECP_IRQ_NBR, (void *)dev); - free_irq(DMA4_TX_IRQ_NBR, (void *)dev); - free_irq(DMA3_RX_IRQ_NBR, (void *)dev); - - return -EAGAIN; - } - -#if 0 - /* We are not allocating DMA since DMA4 is reserved for 'cascading' - * and will always fail with the current dma.c - */ - - /* - * Always allocate the DMA channels after the IRQ, - * and clean up on failure. - */ - - if(request_dma(PAR0_RX_DMA_NBR, cardname)) { - printk("Failed to allocate PAR0_RX_DMA_NBR\n"); - goto grace_exit; - } - - if(request_dma(PAR1_TX_DMA_NBR, cardname)) { - printk("Failed to allocate PAR1_TX_DMA_NBR\n"); - grace_exit: - /* this will cause some 'trying to free free irq' but what the heck... */ - - free_dma(PAR1_TX_DMA_NBR); - free_dma(PAR0_RX_DMA_NBR); - free_irq(PAR0_ECP_IRQ_NBR, (void *)dev); - free_irq(DMA4_TX_IRQ_NBR, (void *)dev); - free_irq(DMA3_RX_IRQ_NBR, (void *)dev); - - return -EAGAIN; - } -#endif - -#ifdef ETHDEBUG - printk("Par port IRQ and DMA allocated\n"); -#endif - save_flags(flags); - cli(); - - /* enable the irq's for PAR0/1 DMA */ - - *R_IRQ_MASK2_SET = - IO_STATE(R_IRQ_MASK2_SET, dma3_eop, set) | - IO_STATE(R_IRQ_MASK2_SET, dma4_descr, set); - - *R_IRQ_MASK0_SET = - IO_STATE(R_IRQ_MASK0_SET, par0_ecp_cmd, set); - - tx_skb = 0; - - /* make sure the irqs are cleared */ - - *R_DMA_CH3_CLR_INTR = IO_STATE(R_DMA_CH3_CLR_INTR, clr_eop, do); - *R_DMA_CH4_CLR_INTR = IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do); - - /* Write the MAC address to the slave HW */ - udelay(5000); - e100_hardware_send_packet(HOST_CMD_SETMAC, dev->dev_addr, 6); - - /* make sure the rec and transmit error counters are cleared */ - - (void)*R_REC_COUNTERS; /* dummy read */ - (void)*R_TR_COUNTERS; /* dummy read */ - - /* start the receiving DMA channel so we can receive packets from now on */ - - *R_DMA_CH3_FIRST = virt_to_phys(myNextRxDesc); - *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, start); - - restore_flags(flags); - - /* We are now ready to accept transmit requeusts from - * the queueing layer of the networking. - */ -#ifdef ETHDEBUG - printk("Starting slave network transmit queue\n"); -#endif - netif_start_queue(dev); - - return 0; -} - -static void -e100_reset_transceiver(void) -{ - /* To do: Reboot and setup slave Etrax */ -} - -/* Called by upper layers if they decide it took too long to complete - * sending a packet - we need to reset and stuff. - */ - -static void -e100_tx_timeout(struct net_device *dev) -{ - struct net_local *np = (struct net_local *)dev->priv; - - printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, - tx_done(dev) ? "IRQ problem" : "network cable problem"); - - /* remember we got an error */ - - np->stats.tx_errors++; - - /* reset the TX DMA in case it has hung on something */ - - RESET_DMA(4); - WAIT_DMA(4); - - /* Reset the transceiver. */ - - e100_reset_transceiver(); - - /* and get rid of the packet that never got an interrupt */ - - dev_kfree_skb(tx_skb); - tx_skb = 0; - - /* tell the upper layers we're ok again */ - - netif_wake_queue(dev); -} - - -/* This will only be invoked if the driver is _not_ in XOFF state. - * What this means is that we need not check it, and that this - * invariant will hold if we make sure that the netif_*_queue() - * calls are done at the proper times. - */ - -static int -e100_send_packet(struct sk_buff *skb, struct net_device *dev) -{ - struct net_local *np = (struct net_local *)dev->priv; - int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - unsigned char *buf = skb->data; - -#ifdef ETHDEBUG - unsigned char *temp_data_ptr = buf; - int i; - - printk("Sending a packet of length %d:\n", length); - /* dump the first bytes in the packet */ - for(i = 0; i < 8; i++) { - printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8, - temp_data_ptr[0],temp_data_ptr[1],temp_data_ptr[2], - temp_data_ptr[3],temp_data_ptr[4],temp_data_ptr[5], - temp_data_ptr[6],temp_data_ptr[7]); - temp_data_ptr += 8; - } -#endif - spin_lock_irq(&np->lock); /* protect from tx_interrupt */ - - tx_skb = skb; /* remember it so we can free it in the tx irq handler later */ - dev->trans_start = jiffies; - - e100_hardware_send_packet(HOST_CMD_SENDPACK, buf, length); - - /* this simple TX driver has only one send-descriptor so we're full - * directly. If this had a send-ring instead, we would only do this if - * the ring got full. - */ - - netif_stop_queue(dev); - - spin_unlock_irq(&np->lock); - - return 0; -} - -/* - * The typical workload of the driver: - * Handle the network interface interrupts. - */ - -static void -e100rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - unsigned long irqbits = *R_IRQ_MASK2_RD; - - if(irqbits & IO_STATE(R_IRQ_MASK2_RD, dma3_eop, active)) { - - /* acknowledge the eop interrupt */ - - *R_DMA_CH3_CLR_INTR = IO_STATE(R_DMA_CH3_CLR_INTR, clr_eop, do); - - /* check if one or more complete packets were indeed received */ - - while(*R_DMA_CH3_FIRST != virt_to_phys(myNextRxDesc)) { - /* Take out the buffer and give it to the OS, then - * allocate a new buffer to put a packet in. - */ - e100_rx(dev); - ((struct net_local *)dev->priv)->stats.rx_packets++; - /* restart/continue on the channel, for safety */ - *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, restart); - /* clear dma channel 3 eop/descr irq bits */ - *R_DMA_CH3_CLR_INTR = - IO_STATE(R_DMA_CH3_CLR_INTR, clr_eop, do) | - IO_STATE(R_DMA_CH3_CLR_INTR, clr_descr, do); - - /* now, we might have gotten another packet - so we have to loop back and check if so */ - } - } -} - -/* the transmit dma channel interrupt - * - * this is supposed to free the skbuff which was pending during transmission, - * and inform the kernel that we can send one more buffer - */ - -static void -e100tx_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - unsigned long irqbits = *R_IRQ_MASK2_RD; - struct net_local *np = (struct net_local *)dev->priv; - -#ifdef ETHDEBUG - printk("We got tx interrupt\n"); -#endif - /* check for a dma4_eop interrupt */ - if(irqbits & IO_STATE(R_IRQ_MASK2_RD, dma4_descr, active)) { - /* This protects us from concurrent execution of - * our dev->hard_start_xmit function above. - */ - - spin_lock(&np->lock); - - /* acknowledge the eop interrupt */ - - *R_DMA_CH4_CLR_INTR = IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do); - - /* skip *R_DMA_CH4_FIRST == 0 test since we use d_wait... */ - if(tx_skb) { - - np->stats.tx_bytes += tx_skb->len; - np->stats.tx_packets++; - /* dma is ready with the transmission of the data in tx_skb, so now we can release the skb memory */ - dev_kfree_skb_irq(tx_skb); - tx_skb = 0; - netif_wake_queue(dev); - } else { - printk(KERN_WARNING "%s: tx weird interrupt\n", - cardname); - } - - spin_unlock(&np->lock); - } -} - -static void -ecp_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct net_local *lp = (struct net_local *)dev->priv; - unsigned long temp, irqbits = *R_IRQ_MASK0_RD; - - /* check for ecp irq */ - if(irqbits & IO_MASK(R_IRQ_MASK0_RD, par0_ecp_cmd)) { - /* acknowledge by reading the bit */ - temp = *R_PAR0_STATUS_DATA; - /* force an EOP on the incoming channel, so we'll get an rx interrupt */ - *R_SET_EOP = IO_STATE(R_SET_EOP, ch3_eop, set); - } -} - -/* We have a good packet(s), get it/them out of the buffers. */ -static void -e100_rx(struct net_device *dev) -{ - struct sk_buff *skb; - int length=0; - int i; - struct net_local *np = (struct net_local *)dev->priv; - struct etrax_dma_descr *mySaveRxDesc = myNextRxDesc; - unsigned char *skb_data_ptr; - - /* If the packet is broken down in many small packages then merge - * count how much space we will need to alloc with skb_alloc() for - * it to fit. - */ - - while (!(myNextRxDesc->status & d_eop)) { - length += myNextRxDesc->sw_len; /* use sw_len for the first descs */ - myNextRxDesc->status = 0; - myNextRxDesc = phys_to_virt(myNextRxDesc->next); - } - - length += myNextRxDesc->hw_len; /* use hw_len for the last descr */ - -#ifdef ETHDEBUG - printk("Got a packet of length %d:\n", length); - /* dump the first bytes in the packet */ - skb_data_ptr = (unsigned char *)phys_to_virt(mySaveRxDesc->buf); - for(i = 0; i < 8; i++) { - printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8, - skb_data_ptr[0],skb_data_ptr[1],skb_data_ptr[2],skb_data_ptr[3], - skb_data_ptr[4],skb_data_ptr[5],skb_data_ptr[6],skb_data_ptr[7]); - skb_data_ptr += 8; - } -#endif - - skb = dev_alloc_skb(length - ETHER_HEAD_LEN); - if (!skb) { - np->stats.rx_errors++; - printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", - dev->name); - return; - } - - skb_put(skb, length - ETHER_HEAD_LEN); /* allocate room for the packet body */ - skb_data_ptr = skb_push(skb, ETHER_HEAD_LEN); /* allocate room for the header */ - -#ifdef ETHDEBUG - printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n", - skb->head, skb->data, skb->tail, skb->end); - printk("copying packet to 0x%x.\n", skb_data_ptr); -#endif - - /* this loop can be made using max two memcpy's if optimized */ - - while(mySaveRxDesc != myNextRxDesc) { - memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf), - mySaveRxDesc->sw_len); - skb_data_ptr += mySaveRxDesc->sw_len; - mySaveRxDesc = phys_to_virt(mySaveRxDesc->next); - } - - memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf), - mySaveRxDesc->hw_len); - - skb->dev = dev; - skb->protocol = eth_type_trans(skb, dev); - - /* Send the packet to the upper layers */ - - netif_rx(skb); - - /* Prepare for next packet */ - - myNextRxDesc->status = 0; - myPrevRxDesc = myNextRxDesc; - myNextRxDesc = phys_to_virt(myNextRxDesc->next); - - myPrevRxDesc->ctrl |= d_eol; - myLastRxDesc->ctrl &= ~d_eol; - myLastRxDesc = myPrevRxDesc; - - return; -} - -/* The inverse routine to net_open(). */ -static int -e100_close(struct net_device *dev) -{ - struct net_local *np = (struct net_local *)dev->priv; - - printk("Closing %s.\n", dev->name); - - netif_stop_queue(dev); - - *R_IRQ_MASK0_CLR = IO_STATE(R_IRQ_MASK0_CLR, par0_ecp_cmd, clr); - - *R_IRQ_MASK2_CLR = - IO_STATE(R_IRQ_MASK2_CLR, dma3_eop, clr) | - IO_STATE(R_IRQ_MASK2_CLR, dma4_descr, clr); - - /* Stop the receiver and the transmitter */ - - RESET_DMA(3); - RESET_DMA(4); - - /* Flush the Tx and disable Rx here. */ - - free_irq(DMA3_RX_IRQ_NBR, (void *)dev); - free_irq(DMA4_TX_IRQ_NBR, (void *)dev); - free_irq(PAR0_ECP_IRQ_NBR, (void *)dev); - - free_dma(PAR1_TX_DMA_NBR); - free_dma(PAR0_RX_DMA_NBR); - - /* Update the statistics here. */ - - update_rx_stats(&np->stats); - update_tx_stats(&np->stats); - - return 0; -} - -static void -update_rx_stats(struct net_device_stats *es) -{ - unsigned long r = *R_REC_COUNTERS; - /* update stats relevant to reception errors */ - es->rx_fifo_errors += r >> 24; /* fifo overrun */ - es->rx_crc_errors += r & 0xff; /* crc error */ - es->rx_frame_errors += (r >> 8) & 0xff; /* alignment error */ - es->rx_length_errors += (r >> 16) & 0xff; /* oversized frames */ -} - -static void -update_tx_stats(struct net_device_stats *es) -{ - unsigned long r = *R_TR_COUNTERS; - /* update stats relevant to transmission errors */ - es->collisions += (r & 0xff) + ((r >> 8) & 0xff); /* single_col + multiple_col */ - es->tx_errors += (r >> 24) & 0xff; /* deferred transmit frames */ -} - -/* - * Get the current statistics. - * This may be called with the card open or closed. - */ -static struct net_device_stats * -e100_get_stats(struct net_device *dev) -{ - struct net_local *lp = (struct net_local *)dev->priv; - - update_rx_stats(&lp->stats); - update_tx_stats(&lp->stats); - - return &lp->stats; -} - -/* - * Set or clear the multicast filter for this adaptor. - * num_addrs == -1 Promiscuous mode, receive all packets - * num_addrs == 0 Normal mode, clear multicast list - * num_addrs > 0 Multicast mode, receive normal and MC packets, - * and do best-effort filtering. - */ -static void -set_multicast_list(struct net_device *dev) -{ - /* To do */ -} - -void -e100_hardware_send_packet(unsigned long hostcmd, char *buf, int length) -{ - static char bogus_ecp[] = { 42, 42 }; - int i; - - -#ifdef ETHDEBUG - printk("e100 send pack, buf 0x%x len %d\n", buf, length); -#endif - - host_command = hostcmd; - - /* Configure the tx dma descriptor. Desc 0 is already configured.*/ - - TxDescList[1].sw_len = length; - /* bug workaround - etrax100 needs d_wait on the descriptor _before_ - * a descriptor containing an ECP command - */ - TxDescList[1].ctrl = d_wait; - TxDescList[1].buf = virt_to_phys(buf); - TxDescList[1].next = virt_to_phys(&TxDescList[2]); - - /* append the ecp dummy descriptor - its only purpose is to - * make the receiver generate an irq due to the ecp command - * so the receiver knows where packets end - */ - - TxDescList[2].sw_len = 1; - TxDescList[2].ctrl = d_ecp | d_eol | d_int; - TxDescList[2].buf = virt_to_phys(bogus_ecp); - - - /* setup the dma channel and start it */ - - *R_DMA_CH4_FIRST = virt_to_phys(TxDescList); - *R_DMA_CH4_CMD = IO_STATE(R_DMA_CH4_CMD, cmd, start); - -#ifdef ETHDEBUG - printk("done\n"); -#endif -} - -/* send a chunk of code to the slave chip to boot it. */ - -static void -boot_slave(unsigned char *code) -{ - int i; - -#ifdef ETHDEBUG - printk(" booting slave ETRAX...\n"); -#endif - *R_PORT_PB_DATA = 0x7F; /* Reset slave */ - udelay(15); /* Time enough to reset WAN tranciever */ - *R_PORT_PB_DATA = 0xFF; /* Reset slave */ - - /* configure the tx dma data descriptor */ - - TxDescList[1].sw_len = ETRAX_PAR_BOOT_LENGTH; - TxDescList[1].ctrl = d_eol | d_int; - - TxDescList[1].buf = virt_to_phys(code); - TxDescList[1].next = 0; - - /* setup the dma channel and start it */ - *R_DMA_CH4_FIRST = virt_to_phys(&TxDescList[1]); - *R_DMA_CH4_CMD = IO_STATE(R_DMA_CH4_CMD, cmd, start); - - /* wait for completion */ - while(!(*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma4_descr))); - - /* ack the irq */ - - *R_DMA_CH4_CLR_INTR = IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do); - -#if 0 - /* manual transfer of boot code - requires dma turned off */ - for (i=0; i<ETRAX_PAR_BOOT_LENGTH; i++) - { - printk(" sending byte: %u value: %x\n",i,code[i]); - while (((*R_PAR1_STATUS)&0x02) == 0); /* Wait while tr_rdy is busy*/ - *R_PAR1_CTRL_DATA = code[i]; - } -#endif - -#ifdef ETHDEBUG - printk(" done\n"); -#endif -} - -#ifdef ETHDEBUG -/* debug code to check the current status of PAR1 */ -static void -dump_parport_status(void) -{ - unsigned long temp; - - printk("Parport1 status:\n"); - - temp = (*R_PAR1_STATUS)&0xE000; - temp = temp >> 13; - printk("Reg mode: %u (ecp_fwd(5), ecp_rev(6))\n", temp); - - temp = (*R_PAR1_STATUS)&0x1000; - temp = temp >> 12; - printk("Reg perr: %u (ecp_rev(0))\n", temp); - - temp = (*R_PAR1_STATUS)&0x0800; - temp = temp >> 11; - printk("Reg ack: %u (inactive (1), active (0))\n", temp); - - temp = (*R_PAR1_STATUS)&0x0400; - temp = temp >> 10; - printk("Reg busy: %u (inactive (0), active (1))\n", temp); - - temp = (*R_PAR1_STATUS)&0x0200; - temp = temp >> 9; - printk("Reg fault: %u (inactive (1), active (0))\n", temp); - - temp = (*R_PAR1_STATUS)&0x0100; - temp = temp >> 8; - printk("Reg sel: %u (inactive (0), active (1), xflag(1))\n", temp); - - temp = (*R_PAR1_STATUS)&0x02; - temp = temp >> 1; - printk("Reg tr_rdy: %u (busy (0), ready (1))\n", temp); - -} -#endif /* ETHDEBUG */ - -static int -etrax_init_module(void) -{ - return etrax_ethernet_lpslave_init(); -} - -module_init(etrax_init_module); diff --git a/arch/cris/drivers/parport.c b/arch/cris/drivers/parport.c deleted file mode 100644 index 443e51a111b8..000000000000 --- a/arch/cris/drivers/parport.c +++ /dev/null @@ -1,572 +0,0 @@ -/* $Id: parport.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ - * - * Elinux parallel port driver - * 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 - * - * Author: Fredrik Hugosson - * - */ - - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/parport.h> -#include <linux/ioport.h> -#include <linux/config.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/sched.h> - -#include <linux/slab.h> -#include <linux/interrupt.h> - -#include <asm/setup.h> -#include <asm/irq.h> -#include <asm/io.h> - -#include <asm/segment.h> -#include <asm/system.h> - -#include <asm/svinto.h> - - -#undef DEBUG -#ifdef DEBUG -#define DPRINTK printk -#else -static inline int DPRINTK(void *nothing, ...) {return 0;} -#endif - -/* - * Etrax100 DMAchannels: - * Par0 out : DMA2 - * Par0 in : DMA3 - * Par1 out : DMA4 - * Par1 in : DMA5 - * NOTE! par0 is shared with ser2 and par1 is shared with ser3 regarding - * DMA and DMA irq - */ - -//#define CONFIG_PAR0_INT 1 -//#define CONFIG_PAR1_INT 1 - -#define SETF(var, reg, field, val) \ - var = (var & ~IO_MASK(##reg##, field)) | IO_FIELD(##reg##, field, val) - -#define SETS(var, reg, field, val) \ - var = (var & ~IO_MASK(##reg##, field)) | IO_STATE(##reg##, field, val) - -struct etrax100par_struct { - /* parallell port control */ - volatile u32 *reg_ctrl_data; /* R_PARx_CTRL_DATA */ - const volatile u32 *reg_status_data; /* R_PARx_STATUS_DATA */ - volatile u32 *reg_config; /* R_PARx_CONFIG */ - volatile u32 *reg_delay; /* R_PARx_DELAY */ - - /* DMA control */ - int odma; - unsigned long dma_irq; /* bitnr in R_IRQ_MASK2 for dmaX_descr */ - - volatile char *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR, output */ - volatile u32 *ofirstadr; /* adr to R_DMA_CHx_FIRST, output */ - volatile char *ocmdadr; /* adr to R_DMA_CHx_CMD, output */ - - volatile char *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR, input */ - volatile u32 *ifirstadr; /* adr to R_DMA_CHx_FIRST, input */ - volatile char *icmdadr; /* adr to R_DMA_CHx_CMD, input */ - - /* Non DMA interrupt stuff */ - unsigned long int_irq; /* R_VECT_MASK_RD */ - const volatile u32 *irq_mask_rd; /* R_IRQ_MASKX_RD */ - volatile u32 *irq_mask_clr; /* R_IRQ_MASKX_RD */ - const volatile u32 *irq_read; /* R_IRQ_READX */ - volatile u32 *irq_mask_set; /* R_IRQ_MASKX_SET */ - unsigned long irq_mask_tx; /* bitmask in R_IRQ_ for tx (ready) int */ - unsigned long irq_mask_rx; /* bitmask in R_IRQ_ for rx (data) int */ - unsigned long irq_mask_ecp_cmd; /* mask in R_IRQ_ for ecp_cmd int */ - unsigned long irq_mask_peri; /* bitmask in R_IRQ_ for peri int */ - int portnr; - - /* ----- end of fields initialised in port_table[] below ----- */ - - struct parport *port; - - /* Shadow registers */ - volatile unsigned long reg_ctrl_data_shadow; /* for R_PARx_CTRL_DATA */ - volatile unsigned long reg_config_shadow; /* for R_PARx_CONFIG */ - volatile unsigned long reg_delay_shadow; /* for R_PARx_DELAY */ -}; - -/* Always have the complete structs here, even if the port is not used! - * (that way we can index this by the port number) - */ -static struct etrax100par_struct port_table[] = { - { - R_PAR0_CTRL_DATA, - R_PAR0_STATUS_DATA, - R_PAR0_CONFIG, - R_PAR0_DELAY, - /* DMA interrupt stuff */ - 2, - 1U << 4, /* uses DMA 2 and 3 */ - R_DMA_CH2_CLR_INTR, - R_DMA_CH2_FIRST, - R_DMA_CH2_CMD, - R_DMA_CH3_CLR_INTR, - R_DMA_CH3_FIRST, - R_DMA_CH3_CMD, - /* Non DMA interrupt stuff */ - IO_BITNR(R_VECT_MASK_RD, par0), - R_IRQ_MASK0_RD, - R_IRQ_MASK0_CLR, - R_IRQ_READ0, - R_IRQ_MASK0_SET, - IO_FIELD(R_IRQ_MASK0_RD, par0_ready, 1U), /* tx (ready)*/ - IO_FIELD(R_IRQ_MASK0_RD, par0_data, 1U), /* rx (data)*/ - IO_FIELD(R_IRQ_MASK0_RD, par0_ecp_cmd, 1U), /* ecp_cmd */ - IO_FIELD(R_IRQ_MASK0_RD, par0_peri, 1U), /* peri */ - 0 - }, - { - R_PAR1_CTRL_DATA, - R_PAR1_STATUS_DATA, - R_PAR1_CONFIG, - R_PAR1_DELAY, - /* DMA interrupt stuff */ - 4, - 1U << 8, /* uses DMA 4 and 5 */ - - R_DMA_CH4_CLR_INTR, - R_DMA_CH4_FIRST, - R_DMA_CH4_CMD, - R_DMA_CH5_CLR_INTR, - R_DMA_CH5_FIRST, - R_DMA_CH5_CMD, - /* Non DMA interrupt stuff */ - IO_BITNR(R_VECT_MASK_RD, par1), - R_IRQ_MASK1_RD, - R_IRQ_MASK1_CLR, - R_IRQ_READ1, - R_IRQ_MASK1_SET, - IO_FIELD(R_IRQ_MASK1_RD, par1_ready, 1U), /* tx (ready)*/ - IO_FIELD(R_IRQ_MASK1_RD, par1_data, 1U), /* rx (data)*/ - IO_FIELD(R_IRQ_MASK1_RD, par1_ecp_cmd, 1U), /* ecp_cmd */ - IO_FIELD(R_IRQ_MASK1_RD, par1_peri, 1U), /* peri */ - 1 - } -}; - - -#define NR_PORTS (sizeof(port_table)/sizeof(struct etrax100par_struct)) - -static void -parport_etrax_write_data(struct parport *p, unsigned char value) -{ - struct etrax100par_struct *info = - (struct etrax100par_struct *)p->private_data; - - DPRINTK("* E100 PP %d: etrax_write_data %02X\n", p->portnum, value); - SETF(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, data, value); - *info->reg_ctrl_data = info->reg_ctrl_data_shadow; -} - - -static unsigned char -parport_etrax_read_data(struct parport *p) -{ - unsigned char ret; - struct etrax100par_struct *info = - (struct etrax100par_struct *)p->private_data; - - ret = IO_EXTRACT(R_PAR0_STATUS_DATA, data, *info->reg_status_data); - - DPRINTK("* E100 PP %d: etrax_read_data %02X\n", p->portnum, ret); - return ret; -} - - -static void -parport_etrax_write_control(struct parport *p, unsigned char control) -{ - struct etrax100par_struct *info = - (struct etrax100par_struct *)p->private_data; - - DPRINTK("* E100 PP %d: etrax_write_control %02x\n", p->portnum, control); - - SETF(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, strb, - (control & PARPORT_CONTROL_STROBE) > 0); - SETF(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, autofd, - (control & PARPORT_CONTROL_AUTOFD) > 0); - SETF(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, init, - (control & PARPORT_CONTROL_INIT) > 0); - SETF(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, seli, - (control & PARPORT_CONTROL_SELECT) > 0); - - *info->reg_ctrl_data = info->reg_ctrl_data_shadow; -} - - -static unsigned char -parport_etrax_read_control( struct parport *p) -{ - unsigned char ret = 0; - struct etrax100par_struct *info = - (struct etrax100par_struct *)p->private_data; - - if (IO_EXTRACT(R_PAR0_CTRL_DATA, strb, info->reg_ctrl_data_shadow)) - ret |= PARPORT_CONTROL_STROBE; - if (IO_EXTRACT(R_PAR0_CTRL_DATA, autofd, info->reg_ctrl_data_shadow)) - ret |= PARPORT_CONTROL_AUTOFD; - if (IO_EXTRACT(R_PAR0_CTRL_DATA, init, info->reg_ctrl_data_shadow)) - ret |= PARPORT_CONTROL_INIT; - if (IO_EXTRACT(R_PAR0_CTRL_DATA, seli, info->reg_ctrl_data_shadow)) - ret |= PARPORT_CONTROL_SELECT; - - DPRINTK("* E100 PP %d: etrax_read_control %02x\n", p->portnum, ret); - return ret; -} - - -static unsigned char -parport_etrax_frob_control(struct parport *p, unsigned char mask, - unsigned char val) -{ - unsigned char old; - - DPRINTK("* E100 PP %d: frob_control mask %02x, value %02x\n", - p->portnum, mask, val); - old = parport_etrax_read_control(p); - parport_etrax_write_control(p, (old & ~mask) ^ val); - return old; -} - - -static unsigned char -parport_etrax_read_status(struct parport *p) -{ - unsigned char ret = 0; - struct etrax100par_struct *info = - (struct etrax100par_struct *)p->private_data; - - if (IO_EXTRACT(R_PAR0_STATUS_DATA, fault, *info->reg_status_data)) - ret |= PARPORT_STATUS_ERROR; - if (IO_EXTRACT(R_PAR0_STATUS_DATA, sel, *info->reg_status_data)) - ret |= PARPORT_STATUS_SELECT; - if (!IO_EXTRACT(R_PAR0_STATUS_DATA, perr, *info->reg_status_data)) - ret |= PARPORT_STATUS_PAPEROUT; - if (IO_EXTRACT(R_PAR0_STATUS_DATA, ack, *info->reg_status_data)) - ret |= PARPORT_STATUS_ACK; - if (!IO_EXTRACT(R_PAR0_STATUS_DATA, busy, *info->reg_status_data)) - ret |= PARPORT_STATUS_BUSY; - - DPRINTK("* E100 PP %d: status register %04x\n", - p->portnum, *info->reg_status_data); - DPRINTK("* E100 PP %d: read_status %02x\n", p->portnum, ret); - return ret; -} - - -static void -parport_etrax_enable_irq(struct parport *p) -{ - struct etrax100par_struct *info = - (struct etrax100par_struct *)p->private_data; - *info->irq_mask_set = info->irq_mask_tx; - DPRINTK("* E100 PP %d: enable irq\n", p->portnum); -} - - -static void -parport_etrax_disable_irq(struct parport *p) -{ - struct etrax100par_struct *info = - (struct etrax100par_struct *)p->private_data; - *info->irq_mask_clr = info->irq_mask_tx; - DPRINTK("* E100 PP %d: disable irq\n", p->portnum); -} - - -static void -parport_etrax_data_forward(struct parport *p) -{ - struct etrax100par_struct *info = - (struct etrax100par_struct *)p->private_data; - - DPRINTK("* E100 PP %d: forward mode\n", p->portnum); - SETS(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, oe, enable); - *info->reg_ctrl_data = info->reg_ctrl_data_shadow; -} - - -static void -parport_etrax_data_reverse(struct parport *p) -{ - struct etrax100par_struct *info = - (struct etrax100par_struct *)p->private_data; - - DPRINTK("* E100 PP %d: reverse mode\n", p->portnum); - SETS(info->reg_ctrl_data_shadow, R_PAR0_CTRL_DATA, oe, disable); - *info->reg_ctrl_data = info->reg_ctrl_data_shadow; -} - - -static void -parport_etrax_init_state(struct pardevice *dev, struct parport_state *s) -{ - DPRINTK("* E100 PP: parport_etrax_init_state\n"); -} - - -static void -parport_etrax_save_state(struct parport *p, struct parport_state *s) -{ - DPRINTK("* E100 PP: parport_etrax_save_state\n"); -} - - -static void -parport_etrax_restore_state(struct parport *p, struct parport_state *s) -{ - DPRINTK("* E100 PP: parport_etrax_restore_state\n"); -} - - -static void -parport_etrax_inc_use_count(void) -{ - MOD_INC_USE_COUNT; -} - - -static void -parport_etrax_dec_use_count(void) -{ - MOD_DEC_USE_COUNT; -} - - -static struct -parport_operations pp_etrax_ops = { - parport_etrax_write_data, - parport_etrax_read_data, - - parport_etrax_write_control, - parport_etrax_read_control, - parport_etrax_frob_control, - - parport_etrax_read_status, - - parport_etrax_enable_irq, - parport_etrax_disable_irq, - - parport_etrax_data_forward, - parport_etrax_data_reverse, - - parport_etrax_init_state, - parport_etrax_save_state, - parport_etrax_restore_state, - - parport_etrax_inc_use_count, - parport_etrax_dec_use_count, - - parport_ieee1284_epp_write_data, - parport_ieee1284_epp_read_data, - parport_ieee1284_epp_write_addr, - parport_ieee1284_epp_read_addr, - - parport_ieee1284_ecp_write_data, - parport_ieee1284_ecp_read_data, - parport_ieee1284_ecp_write_addr, - - parport_ieee1284_write_compat, - parport_ieee1284_read_nibble, - parport_ieee1284_read_byte, -}; - - -static void -parport_etrax_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct etrax100par_struct *info = (struct etrax100par_struct *) - ((struct parport *)dev_id)->private_data; - DPRINTK("* E100 PP %d: Interrupt received\n", - ((struct parport *)dev_id)->portnum); - *info->irq_mask_clr = info->irq_mask_tx; - parport_generic_irq(irq, (struct parport *)dev_id, regs); -} - -/* ----------- Initialisation code --------------------------------- */ - -static void __init -parport_etrax_show_parallel_version(void) -{ - printk("ETRAX 100LX parallel port driver v1.0, (c) 2001 Axis Communications AB\n"); -} - -#ifdef CONFIG_ETRAX_PAR0_DMA -#define PAR0_USE_DMA 1 -#else -#define PAR0_USE_DMA 0 -#endif - -#ifdef CONFIG_ETRAX_PAR1_DMA -#define PAR1_USE_DMA 1 -#else -#define PAR1_USE_DMA 0 -#endif - -static void __init -parport_etrax_init_registers(void) -{ - struct etrax100par_struct *info; - int i; - - for (i = 0, info = port_table; i < 2; i++, info++) { -#ifndef CONFIG_ETRAX_PARALLEL_PORT0 - if (i == 0) - continue; -#endif -#ifndef CONFIG_ETRAX_PARALLEL_PORT1 - if (i == 1) - continue; -#endif - info->reg_config_shadow = - IO_STATE(R_PAR0_CONFIG, iseli, inv) | - IO_STATE(R_PAR0_CONFIG, iautofd, inv) | - IO_STATE(R_PAR0_CONFIG, istrb, inv) | - IO_STATE(R_PAR0_CONFIG, iinit, inv) | - IO_STATE(R_PAR0_CONFIG, rle_in, disable) | - IO_STATE(R_PAR0_CONFIG, rle_out, disable) | - IO_STATE(R_PAR0_CONFIG, enable, on) | - IO_STATE(R_PAR0_CONFIG, force, off) | - IO_STATE(R_PAR0_CONFIG, ign_ack, wait) | - IO_STATE(R_PAR0_CONFIG, oe_ack, wait_oe) | - IO_STATE(R_PAR0_CONFIG, mode, manual); - - if ((i == 0 && PAR0_USE_DMA) || (i == 1 && PAR1_USE_DMA)) - info->reg_config_shadow |= - IO_STATE(R_PAR0_CONFIG, dma, enable); - else - info->reg_config_shadow |= - IO_STATE(R_PAR0_CONFIG, dma, disable); - - *info->reg_config = info->reg_config_shadow; - - info->reg_ctrl_data_shadow = - IO_STATE(R_PAR0_CTRL_DATA, peri_int, nop) | - IO_STATE(R_PAR0_CTRL_DATA, oe, enable) | - IO_STATE(R_PAR0_CTRL_DATA, seli, inactive) | - IO_STATE(R_PAR0_CTRL_DATA, autofd, inactive) | - IO_STATE(R_PAR0_CTRL_DATA, strb, inactive) | - IO_STATE(R_PAR0_CTRL_DATA, init, inactive) | - IO_STATE(R_PAR0_CTRL_DATA, ecp_cmd, data) | - IO_FIELD(R_PAR0_CTRL_DATA, data, 0); - *info->reg_ctrl_data = info->reg_ctrl_data_shadow; - - /* Clear peri int without setting shadow */ - *info->reg_ctrl_data = info->reg_ctrl_data_shadow | - IO_STATE(R_PAR0_CTRL_DATA, peri_int, ack); - - info->reg_delay_shadow = - IO_FIELD(R_PAR0_DELAY, setup, 5) | - IO_FIELD(R_PAR0_DELAY, strobe, 5) | - IO_FIELD(R_PAR0_DELAY, hold, 5); - *info->reg_delay = info->reg_delay_shadow; - } - -#ifdef CONFIG_ETRAX_PARALLEL_PORT0 -#ifdef CONFIG_ETRAX_PAR0_DMA - RESET_DMA(PAR0_TX_DMA_NBR); - WAIT_DMA(PAR0_TX_DMA_NBR); -#ifdef CONFIG_ETRAX_SERIAL_PORT2 - printk(" Warning - DMA clash with ser2!\n"); -#endif /* SERIAL_PORT2 */ -#endif /* DMA */ -#endif /* PORT0 */ - -#ifdef CONFIG_ETRAX_PARALLEL_PORT1 -#ifdef CONFIG_ETRAX_PAR1_DMA - RESET_DMA(PAR1_TX_DMA_NBR); - WAIT_DMA(PAR1_TX_DMA_NBR); -#ifdef CONFIG_ETRAX_SERIAL_PORT3 - printk(" Warning - DMA clash with ser3!\n"); -#endif /* SERIAL_PORT3 */ -#endif /* DMA */ -#endif /* PORT1 */ -} - - -int __init -parport_etrax_init(void) -{ - struct parport *p; - int port_exists = 0; - int i; - struct etrax100par_struct *info; - const char *names[] = { "parallel 0 tx+rx", "parallel 1 tx+rx" }; - - parport_etrax_show_parallel_version(); - parport_etrax_init_registers(); - - for (i = 0, info = port_table; i < NR_PORTS; i++, info++) { -#ifndef CONFIG_ETRAX_PARALLEL_PORT0 - if (i == 0) - continue; -#endif -#ifndef CONFIG_ETRAX_PARALLEL_PORT1 - if (i == 1) - continue; -#endif - p = parport_register_port((unsigned long)0, info->int_irq, - PARPORT_DMA_NONE, &pp_etrax_ops); - if (!p) - continue; - - info->port = p; - p->private_data = info; - /* Axis FIXME: Set mode flags. */ - /* p->modes = PARPORT_MODE_TRISTATE | PARPORT_MODE_SAFEININT; */ - - if(request_irq(info->int_irq, parport_etrax_interrupt, - SA_SHIRQ, names[i], p)) { - parport_unregister_port (p); - continue; - } - - printk(KERN_INFO "%s: ETRAX 100LX port %d using irq\n", - p->name, i); - parport_proc_register(p); - parport_announce_port(p); - port_exists = 1; - } - - return port_exists; -} - -void __exit -parport_etrax_exit(void) -{ - int i; - struct etrax100par_struct *info; - - for (i = 0, info = port_table; i < NR_PORTS; i++, info++) { -#ifndef CONFIG_ETRAX_PARALLEL_PORT0 - if (i == 0) - continue; -#endif -#ifndef CONFIG_ETRAX_PARALLEL_PORT1 - if (i == 1) - continue; -#endif - if (info->int_irq != PARPORT_IRQ_NONE) - free_irq(info->int_irq, info->port); - parport_proc_unregister(info->port); - parport_unregister_port(info->port); - } -} diff --git a/arch/cris/drivers/sync_serial.c b/arch/cris/drivers/sync_serial.c deleted file mode 100644 index 59d62e01c731..000000000000 --- a/arch/cris/drivers/sync_serial.c +++ /dev/null @@ -1,900 +0,0 @@ -/* - * Simple synchronous serial port driver for ETRAX 100LX. - * - * Synchronous serial ports are used for continous 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 - * - * Copyright (c) 2001 Axis Communications AB - * - * Author: Mikael Starvik - * - */ -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/config.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/major.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/timer.h> -#include <asm/irq.h> -#include <asm/io.h> -#include <asm/svinto.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <asm/sync_serial.h> - -/* The receiver is a bit tricky beacuse of the continous stream of data. */ -/* */ -/* Two DMA descriptors are linked together. Each DMA descriptor is */ -/* responsible for one half of a common buffer. */ -/* */ -/* ------------------------------ */ -/* | ---------- ---------- | */ -/* --> | Descr1 |-->| Descr2 |--- */ -/* ---------- ---------- */ -/* | | */ -/* v v */ -/* ----------------------------- */ -/* | BUFFER | */ -/* ----------------------------- */ -/* | | */ -/* readp writep */ -/* */ -/* If the application keeps up the pace readp will be right after writep.*/ -/* If the application can't keep the pace we have to throw away data. */ -/* The idea is that readp should be ready with the data pointed out by */ -/* Descr1 when the DMA has filled in Descr2. Otherwise we will discard */ -/* the rest of the data pointed out by Descr1 and set readp to the start */ -/* of Descr2 */ - -#define SYNC_SERIAL_MAJOR 125 - -/* IN_BUFFER_SIZE should be a multiple of 6 to make sure that 24 bit */ -/* words can be handled */ - -#define IN_BUFFER_SIZE 12288 -#define OUT_BUFFER_SIZE 4096 - -#define DEFAULT_FRAME_RATE 0 -#define DEFAULT_WORD_RATE 7 - -#define DEBUG(x) - -/* Define some macros to access ETRAX 100 registers */ -#define SETF(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \ - IO_FIELD(##reg##, field, val) -#define SETS(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \ - IO_STATE(##reg##, field, val) - -typedef struct sync_port -{ - /* Etrax registers and bits*/ - volatile unsigned * const status; - volatile unsigned * const ctrl_data; - volatile unsigned * const output_dma_first; - volatile unsigned char * const output_dma_cmd; - volatile unsigned char * const output_dma_clr_irq; - volatile unsigned * const input_dma_first; - volatile unsigned char * const input_dma_cmd; - volatile unsigned char * const input_dma_clr_irq; - volatile unsigned * const data_out; - volatile unsigned * const data_in; - char data_avail_bit; /* In R_IRQ_MASK1_RD */ - char transmitter_ready_bit; /* In R_IRQ_MASK1_RD */ - char ready_irq_bit; /* In R_IRQ_MASK1_SET and R_IRQ_MASK1_CLR */ - char input_dma_descr_bit; /* In R_IRQ_MASK2_RD */ - char output_dma_bit; /* In R_IRQ_MASK2_RD */ - - int enabled; /* 1 if port is enabled */ - int use_dma; /* 1 if port uses dma */ - int port_nbr; /* Port 0 or 1 */ - unsigned ctrl_data_shadow; /* Register shadow */ - char busy; /* 1 if port is busy */ - wait_queue_head_t out_wait_q; - wait_queue_head_t in_wait_q; - struct etrax_dma_descr out_descr; - struct etrax_dma_descr in_descr1; - struct etrax_dma_descr in_descr2; - char out_buffer[OUT_BUFFER_SIZE]; - int out_count; /* Remaining bytes for current transfer */ - char* outp; /* Current position in out_buffer */ - char in_buffer[IN_BUFFER_SIZE]; - volatile char* readp; /* Next byte to be read by application */ - volatile char* writep; /* Next byte to be written by etrax */ - int odd_output; /* 1 if writing odd nible in 12 bit mode */ - int odd_input; /* 1 if reading odd nible in 12 bit mode */ -} sync_port; - - -static int etrax_sync_serial_init(void); -static void initialize_port(int portnbr); -static int sync_serial_open(struct inode *, struct file*); -static int sync_serial_release(struct inode*, struct file*); -static int sync_serial_ioctl(struct inode*, struct file*, - unsigned int cmd, unsigned long arg); -static ssize_t sync_serial_write(struct file * file, const char * buf, - size_t count, loff_t *ppos); -static ssize_t sync_serial_manual_write(struct file * file, const char * buf, - size_t count, loff_t *ppos); -static ssize_t sync_serial_read(struct file *file, char *buf, - size_t count, loff_t *ppos); -static void send_word(sync_port* port); -static void start_dma(struct sync_port *port, const char* data, int count); -static void start_dma_in(sync_port* port); -static void tr_interrupt(int irq, void *dev_id, struct pt_regs * regs); -static void rx_interrupt(int irq, void *dev_id, struct pt_regs * regs); -static void manual_interrupt(int irq, void *dev_id, struct pt_regs * regs); - -/* The ports */ -static struct sync_port ports[]= -{ - { - R_SYNC_SERIAL1_STATUS, /* status */ - R_SYNC_SERIAL1_CTRL, /* ctrl_data */ - R_DMA_CH8_FIRST, /* output_dma_first */ - R_DMA_CH8_CMD, /* output_dma_cmd */ - R_DMA_CH8_CLR_INTR, /* output_dma_clr_irq */ - R_DMA_CH9_FIRST, /* input_dma_first */ - R_DMA_CH9_CMD, /* input_dma_cmd */ - R_DMA_CH9_CLR_INTR, /* input_dma_clr_irq */ - R_SYNC_SERIAL1_TR_DATA, /* data_out */ - R_SYNC_SERIAL1_REC_DATA,/* data in */ - IO_BITNR(R_IRQ_MASK1_RD, ser1_data), /* data_avail_bit */ - IO_BITNR(R_IRQ_MASK1_RD, ser1_ready), /* transmitter_ready_bit */ - IO_BITNR(R_IRQ_MASK1_SET, ser1_ready), /* ready_irq_bit */ - IO_BITNR(R_IRQ_MASK2_RD, dma9_descr), /* input_dma_descr_bit */ - IO_BITNR(R_IRQ_MASK2_RD, dma8_eop), /* output_dma_bit */ - }, - { - R_SYNC_SERIAL3_STATUS, /* status */ - R_SYNC_SERIAL3_CTRL, /* ctrl_data */ - R_DMA_CH4_FIRST, /* output_dma_first */ - R_DMA_CH4_CMD, /* output_dma_cmd */ - R_DMA_CH4_CLR_INTR, /* output_dma_clr_irq */ - R_DMA_CH5_FIRST, /* input_dma_first */ - R_DMA_CH5_CMD, /* input_dma_cmd */ - R_DMA_CH5_CLR_INTR, /* input_dma_clr_irq */ - R_SYNC_SERIAL3_TR_DATA, /* data_out */ - R_SYNC_SERIAL3_REC_DATA,/* data in */ - IO_BITNR(R_IRQ_MASK1_RD, ser3_data), /* data_avail_bit */ - IO_BITNR(R_IRQ_MASK1_RD, ser3_ready), /* transmitter_ready_bit */ - IO_BITNR(R_IRQ_MASK1_SET, ser3_ready), /* ready_irq_bit */ - IO_BITNR(R_IRQ_MASK2_RD, dma5_descr), /* input_dma_descr_bit */ - IO_BITNR(R_IRQ_MASK2_RD, dma4_eop), /* output_dma_bit */ - } -}; - -/* Register shadows */ -static unsigned sync_serial_prescale_shadow = 0; -static unsigned gen_config_ii_shadow = 0; - -#define NUMBER_OF_PORTS (sizeof(ports)/sizeof(sync_port)) - -static struct file_operations sync_serial_fops = { - .owner = THIS_MODULE, - .write = sync_serial_write, - .read = sync_serial_read, - .ioctl = sync_serial_ioctl, - .open = sync_serial_open, - .release = sync_serial_release -}; - -static int __init etrax_sync_serial_init(void) -{ - ports[0].enabled = 0; - ports[1].enabled = 0; - - if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 ) - { - printk("unable to get major for synchronous serial port\n"); - return -EBUSY; - } - - /* Deselect synchronous serial ports */ - SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async); - SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async); - SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, ser3, select); - *R_GEN_CONFIG_II = gen_config_ii_shadow; - - /* Initialize Ports */ -#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) - ports[0].enabled = 1; - SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser1, ss1extra); - SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync); -#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) - ports[0].use_dma = 1; - initialize_port(0); - if(request_irq(24, tr_interrupt, 0, "synchronous serial 1 dma tr", &ports[0])) - panic("Can't allocate sync serial port 1 IRQ"); - if(request_irq(25, rx_interrupt, 0, "synchronous serial 1 dma rx", &ports[0])) - panic("Can't allocate sync serial port 1 IRQ"); - RESET_DMA(8); WAIT_DMA(8); - RESET_DMA(9); WAIT_DMA(9); - *R_DMA_CH8_CLR_INTR = IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, do) | - IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, do); - *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do) | - IO_STATE(R_DMA_CH9_CLR_INTR, clr_descr, do); - *R_IRQ_MASK2_SET = - IO_STATE(R_IRQ_MASK2_SET, dma8_eop, set) | - IO_STATE(R_IRQ_MASK2_SET, dma8_descr, set) | - IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set); - start_dma_in(&ports[0]); -#else - ports[0].use_dma = 0; - initialize_port(0); - if (request_irq(8, manual_interrupt, SA_SHIRQ, "synchronous serial manual irq", &ports[0])) - panic("Can't allocate sync serial manual irq"); - *R_IRQ_MASK1_SET = IO_STATE(R_IRQ_MASK1_SET, ser1_data, set); -#endif -#endif - -#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) - ports[1].enabled = 1; - SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser3, ss3extra); - SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync); -#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA) - ports[1].use_dma = 1; - initialize_port(1); - if(request_irq(20, tr_interrupt, 0, "synchronous serial 3 dma tr", &ports[1])) - panic("Can't allocate sync serial port 1 IRQ"); - if(request_irq(21, rx_interrupt, 0, "synchronous serial 3 dma rx", &ports[1])) - panic("Can't allocate sync serial port 1 IRQ"); - RESET_DMA(4); WAIT_DMA(4); - RESET_DMA(5); WAIT_DMA(5); - *R_DMA_CH4_CLR_INTR = IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, do) | - IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do); - *R_DMA_CH5_CLR_INTR = IO_STATE(R_DMA_CH5_CLR_INTR, clr_eop, do) | - IO_STATE(R_DMA_CH5_CLR_INTR, clr_descr, do); - *R_IRQ_MASK2_SET = - IO_STATE(R_IRQ_MASK2_SET, dma4_eop, set) | - IO_STATE(R_IRQ_MASK2_SET, dma4_descr, set) | - IO_STATE(R_IRQ_MASK2_SET, dma5_descr, set); - start_dma_in(&ports[1]); -#else - ports[1].use_dma = 0; - initialize_port(1); - if (port[0].use_dma) /* Port 0 uses dma, we must manual allocate IRQ */ - { - if (request_irq(8, manual_interrupt, SA_SHIRQ, "synchronous serial manual irq", &ports[1])) - panic("Can't allocate sync serial manual irq"); - } - *R_IRQ_MASK1_SET = IO_STATE(R_IRQ_MASK1_SET, ser3_data, set); -#endif -#endif - - *R_PORT_PB_I2C = port_pb_i2c_shadow; /* Use PB4/PB7 */ - - /* Set up timing */ - *R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow = ( - IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u1, codec) | - IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u1, external) | - IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u3, codec) | - IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u3, external) | - IO_STATE(R_SYNC_SERIAL_PRESCALE, prescaler, div4) | - IO_FIELD(R_SYNC_SERIAL_PRESCALE, frame_rate, DEFAULT_FRAME_RATE) | - IO_FIELD(R_SYNC_SERIAL_PRESCALE, word_rate, DEFAULT_WORD_RATE) | - IO_STATE(R_SYNC_SERIAL_PRESCALE, warp_mode, normal)); - - /* Select synchronous ports */ - *R_GEN_CONFIG_II = gen_config_ii_shadow; - - printk("ETRAX 100LX synchronous serial port driver\n"); - return 0; -} - -static void initialize_port(int portnbr) -{ - struct sync_port* port = &ports[portnbr]; - - DEBUG(printk("Init sync serial port %d\n", portnbr)); - - port->port_nbr = portnbr; - port->busy = 0; - port->readp = port->in_buffer; - port->writep = port->in_buffer + IN_BUFFER_SIZE/2; - port->odd_input = 0; - - init_waitqueue_head(&port->out_wait_q); - init_waitqueue_head(&port->in_wait_q); - - port->ctrl_data_shadow = - IO_STATE(R_SYNC_SERIAL1_CTRL, tr_baud, c115k2Hz) | - IO_STATE(R_SYNC_SERIAL1_CTRL, mode, master_output) | - IO_STATE(R_SYNC_SERIAL1_CTRL, error, ignore) | - IO_STATE(R_SYNC_SERIAL1_CTRL, rec_enable, disable) | - IO_STATE(R_SYNC_SERIAL1_CTRL, f_synctype, normal) | - IO_STATE(R_SYNC_SERIAL1_CTRL, f_syncsize, word) | - IO_STATE(R_SYNC_SERIAL1_CTRL, f_sync, on) | - IO_STATE(R_SYNC_SERIAL1_CTRL, clk_mode, normal) | - IO_STATE(R_SYNC_SERIAL1_CTRL, clk_halt, stopped) | - IO_STATE(R_SYNC_SERIAL1_CTRL, bitorder, msb) | - IO_STATE(R_SYNC_SERIAL1_CTRL, tr_enable, disable) | - IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit) | - IO_STATE(R_SYNC_SERIAL1_CTRL, buf_empty, lmt_8) | - IO_STATE(R_SYNC_SERIAL1_CTRL, buf_full, lmt_8) | - IO_STATE(R_SYNC_SERIAL1_CTRL, flow_ctrl, enabled) | - IO_STATE(R_SYNC_SERIAL1_CTRL, clk_polarity, neg) | - IO_STATE(R_SYNC_SERIAL1_CTRL, frame_polarity, normal)| - IO_STATE(R_SYNC_SERIAL1_CTRL, status_polarity, inverted)| - IO_STATE(R_SYNC_SERIAL1_CTRL, clk_driver, normal) | - IO_STATE(R_SYNC_SERIAL1_CTRL, frame_driver, normal) | - IO_STATE(R_SYNC_SERIAL1_CTRL, status_driver, normal)| - IO_STATE(R_SYNC_SERIAL1_CTRL, def_out0, high); - - if (port->use_dma) - port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL, dma_enable, on); - else - port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL, dma_enable, off); - - *port->ctrl_data = port->ctrl_data_shadow; -} - -static int sync_serial_open(struct inode *inode, struct file *file) -{ - int dev = MINOR(inode->i_rdev); - DEBUG(printk("Open sync serial port %d\n", dev)); - - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) - { - DEBUG(printk("Invalid minor %d\n", dev)); - return -ENODEV; - } - if (ports[dev].busy) - { - DEBUG(printk("Device is busy.. \n")); - return -EBUSY; - } - ports[dev].busy = 1; - return 0; -} - -static int sync_serial_release(struct inode *inode, struct file *file) -{ - int dev = MINOR(inode->i_rdev); - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) - { - DEBUG(printk("Invalid minor %d\n", dev)); - return -ENODEV; - } - ports[dev].busy = 0; - return 0; -} - -static int sync_serial_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int return_val = 0; - int dev = MINOR(file->f_dentry->d_inode->i_rdev); - sync_port* port; - - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) - { - DEBUG(printk("Invalid minor %d\n", dev)); - return -1; - } - port = &ports[dev]; - - /* Disable port while changing config */ - if (dev) - { - RESET_DMA(4); WAIT_DMA(4); - *R_DMA_CH4_CLR_INTR = IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, do) | - IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do); - SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async); - } - else - { - RESET_DMA(8); WAIT_DMA(8); - *R_DMA_CH8_CLR_INTR = IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, do) | - IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, do); - SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async); - } - *R_GEN_CONFIG_II = gen_config_ii_shadow; - - switch(cmd) - { - case SSP_SPEED: - if (GET_SPEED(arg) == CODEC) - { - if (dev) - SETS(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, clk_sel_u3, codec); - else - SETS(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, clk_sel_u1, codec); - - SETF(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, prescaler, GET_FREQ(arg)); - SETF(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, frame_rate, GET_FRAME_RATE(arg)); - SETF(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, word_rate, GET_WORD_RATE(arg)); - } - else - { - SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_baud, GET_SPEED(arg)); - if (dev) - SETS(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, clk_sel_u3, baudrate); - else - SETS(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, clk_sel_u1, baudrate); - } - break; - case SSP_MODE: - if (arg > 5) - return -EINVAL; - SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, arg); - break; - case SSP_FRAME_SYNC: - if (arg & NORMAL_SYNC) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype, normal); - else if (arg & EARLY_SYNC) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype, early); - - if (arg & BIT_SYNC) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, bit); - else if (arg & WORD_SYNC) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, word); - else if (arg & EXTENDED_SYNC) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, extended); - - if (arg & SYNC_ON) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, on); - else if (arg & SYNC_OFF) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, off); - - if (arg & WORD_SIZE_8) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size8bit); - else if (arg & WORD_SIZE_12) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size12bit); - else if (arg & WORD_SIZE_16) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size16bit); - else if (arg & WORD_SIZE_24) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size24bit); - else if (arg & WORD_SIZE_32) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size32bit); - - if (arg & BIT_ORDER_MSB) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder, msb); - else if (arg & BIT_ORDER_LSB) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder, lsb); - - if (arg & FLOW_CONTROL_ENABLE) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl, enabled); - else if (arg & FLOW_CONTROL_DISABLE) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl, disabled); - - if (arg & CLOCK_NOT_GATED) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_mode, normal); - else if (arg & CLOCK_GATED) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_mode, gated); - - break; - case SSP_IPOLARITY: - if (arg & CLOCK_NORMAL) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_polarity, neg); - else if (arg & CLOCK_INVERT) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_polarity, pos); - - if (arg & FRAME_NORMAL) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_polarity, normal); - else if (arg & FRAME_INVERT) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_polarity, inverted); - - if (arg & STATUS_NORMAL) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, status_polarity, normal); - else if (arg & STATUS_INVERT) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, status_polarity, inverted); - break; - case SSP_OPOLARITY: - if (arg & CLOCK_NORMAL) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_driver, normal); - else if (arg & CLOCK_INVERT) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_driver, inverted); - - if (arg & FRAME_NORMAL) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_driver, normal); - else if (arg & FRAME_INVERT) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_driver, inverted); - - if (arg & STATUS_NORMAL) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, status_driver, normal); - else if (arg & STATUS_INVERT) - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, status_driver, inverted); - break; - case SSP_SPI: - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl, disabled); - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder, msb); - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size8bit); - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, on); - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, word); - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype, normal); - if (arg & SPI_SLAVE) - { - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_polarity, inverted); - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_polarity, neg); - SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, SLAVE_INPUT); - } - else - { - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_driver, inverted); - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_driver, inverted); - SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, MASTER_OUTPUT); - } - break; - default: - return_val = -1; - } - /* Set config and enable port */ - *port->ctrl_data = port->ctrl_data_shadow; - *R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow; - if (dev) - SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync); - else - SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync); - - *R_GEN_CONFIG_II = gen_config_ii_shadow; - return return_val; -} - -static ssize_t sync_serial_manual_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - int dev = MINOR(file->f_dentry->d_inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - sync_port* port; - - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) - { - DEBUG(printk("Invalid minor %d\n", dev)); - return -ENODEV; - } - - port = &ports[dev]; - if (copy_from_user(port->out_buffer, buf, count)) - return -EFAULT; - port->outp = port->out_buffer; - port->out_count = count; - port->odd_output = 1; - add_wait_queue(&port->out_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); - *R_IRQ_MASK1_SET = 1 << port->ready_irq_bit; /* transmitter ready IRQ on */ - send_word(port); /* Start sender by sending first word */ - schedule(); - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->out_wait_q, &wait); - if (signal_pending(current)) - { - return -EINTR; - } - return count; -} - -static ssize_t sync_serial_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - int dev = MINOR(file->f_dentry->d_inode->i_rdev); - DECLARE_WAITQUEUE(wait, current); - sync_port *port; - - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) - { - DEBUG(printk("Invalid minor %d\n", dev)); - return -ENODEV; - } - port = &ports[dev]; - - DEBUG(printk("Write dev %d count %d\n", port->port_nbr, count)); - - count = count > OUT_BUFFER_SIZE ? OUT_BUFFER_SIZE : count; - - /* Make sure transmitter is running */ - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, running); - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, enable); - *port->ctrl_data = port->ctrl_data_shadow; - - if (!port->use_dma) - { - return sync_serial_manual_write(file, buf, count, ppos); - } - - if (copy_from_user(port->out_buffer, buf, count)) - return -EFAULT; - add_wait_queue(&port->out_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); - start_dma(port, buf, count); - schedule(); - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->out_wait_q, &wait); - if (signal_pending(current)) - { - return -EINTR; - } - return count; -} - -static ssize_t sync_serial_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - int dev = MINOR(file->f_dentry->d_inode->i_rdev); - int avail; - sync_port *port; - char* start; - char* end; - unsigned long flags; - - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) - { - DEBUG(printk("Invalid minor %d\n", dev)); - return -ENODEV; - } - port = &ports[dev]; - - DEBUG(printk("Read dev %d count %d\n", dev, count)); - - /* Make sure receiver is running */ - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, running); - SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, enable); - *port->ctrl_data = port->ctrl_data_shadow; - - /* Calculate number of available bytes */ - while (port->readp == port->writep) /* No data */ - { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - interruptible_sleep_on(&port->in_wait_q); - if (signal_pending(current)) - { - return -EINTR; - } - } - - /* Save pointers to avoid that they are modified by interrupt */ - start = port->readp; - end = port->writep; - - /* Lazy read, never return wrapped data. */ - if (end > start) - avail = end - start; - else - avail = port->in_buffer + IN_BUFFER_SIZE - start; - - count = count > avail ? avail : count; - if (copy_to_user(buf, start, count)) - return -EFAULT; - - /* Disable interrupts while updating readp */ - save_flags(flags); - cli(); - port->readp += count; - if (port->readp == port->in_buffer + IN_BUFFER_SIZE) /* Wrap? */ - port->readp = port->in_buffer; - restore_flags(flags); - - DEBUG(printk("%d bytes read\n", count)); - return count; -} - -static void send_word(sync_port* port) -{ - switch(port->ctrl_data_shadow & IO_MASK(R_SYNC_SERIAL1_CTRL, wordsize)) - { - case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit): - port->out_count--; - *port->data_out = *port->outp++; - break; - case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit): - port->out_count--; - if (port->odd_output) - *port->data_out = ((*port->outp) << 16) | (*(unsigned short *)(port->outp + 1)); - else - *port->data_out = ((*(unsigned short *)port->outp) << 8) | (*(port->outp + 1)); - port->odd_output = !port->odd_output; - port->outp++; - break; - case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit): - port->out_count-=2; - *port->data_out = *(unsigned short *)port->outp; - port->outp+=2; - break; - case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit): - port->out_count-=3; - *port->data_out = *(unsigned int *)port->outp; - port->outp+=3; - break; - case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit): - port->out_count-=4; - *port->data_out = *(unsigned int *)port->outp; - port->outp+=4; - break; - } -} - -static void start_dma(struct sync_port* port, const char* data, int count) -{ - port->out_descr.hw_len = 0; - port->out_descr.next = 0; - port->out_descr.ctrl = d_int | d_eol | d_eop | d_wait; - port->out_descr.sw_len = count; - port->out_descr.buf = virt_to_phys(port->out_buffer); - port->out_descr.status = 0; - - *port->output_dma_first = virt_to_phys(&port->out_descr); - *port->output_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start); -} - -static void start_dma_in(sync_port* port) -{ - if (port->writep > port->in_buffer + IN_BUFFER_SIZE) - { - panic("Offset too large in sync serial driver\n"); - return; - } - port->in_descr1.hw_len = 0; - port->in_descr1.ctrl = d_int; - port->in_descr1.status = 0; - port->in_descr1.next = virt_to_phys(&port->in_descr2); - port->in_descr2.hw_len = 0; - port->in_descr2.next = virt_to_phys(&port->in_descr1); - port->in_descr2.ctrl = d_int; - port->in_descr2.status = 0; - - /* Find out which descriptor to start */ - if (port->writep >= port->in_buffer + IN_BUFFER_SIZE/2) - { - /* Start descriptor 2 */ - port->in_descr1.sw_len = IN_BUFFER_SIZE/2; /* All data available in 1 */ - port->in_descr1.buf = virt_to_phys(port->in_buffer); - port->in_descr2.sw_len = port->in_buffer + IN_BUFFER_SIZE - port->writep; - port->in_descr2.buf = virt_to_phys(port->writep); - *port->input_dma_first = virt_to_phys(&port->in_descr2); - } - else - { - /* Start descriptor 1 */ - port->in_descr1.sw_len = port->in_buffer + IN_BUFFER_SIZE/2 - port->writep; - port->in_descr1.buf = virt_to_phys(port->writep); - port->in_descr2.sw_len = IN_BUFFER_SIZE/2; - port->in_descr2.buf = virt_to_phys(port->in_buffer + IN_BUFFER_SIZE / 2); - *port->input_dma_first = virt_to_phys(&port->in_descr1); - } - *port->input_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start); -} - -static void tr_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - unsigned long ireg = *R_IRQ_MASK2_RD; - int i; - - for (i = 0; i < NUMBER_OF_PORTS; i++) - { - sync_port *port = &ports[i]; - if (ireg & (1 << port->output_dma_bit)) /* IRQ active for the port? */ - { - /* Clear IRQ */ - *port->output_dma_clr_irq = - IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do) | - IO_STATE(R_DMA_CH0_CLR_INTR, clr_descr, do); - wake_up_interruptible(&port->out_wait_q); /* wake up the waiting process */ - } - } -} - -static void rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - unsigned long ireg = *R_IRQ_MASK2_RD; - int i; - - for (i = 0; i < NUMBER_OF_PORTS; i++) - { - int update = 0; - sync_port *port = &ports[i]; - - if (!port->enabled) - { - continue; - } - - if (ireg & (1 << port->input_dma_descr_bit)) /* Descriptor interrupt */ - { - /* DMA has reached end of descriptor */ - *port->input_dma_clr_irq = - IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do) | - IO_STATE(R_DMA_CH0_CLR_INTR, clr_descr, do); - - /* Find out which descriptor that is ready */ - if (port->writep >= port->in_buffer + IN_BUFFER_SIZE/2) - { - /* Descr 2 was ready. Restart DMA at descriptor 1 */ - port->writep = port->in_buffer; - - /* Throw away data? */ - if (port->readp < port->in_buffer + IN_BUFFER_SIZE/2) - port->readp = port->in_buffer + IN_BUFFER_SIZE/2; - } - else - { - /* Descr 1 was ready. Restart DMA at descriptor 2 */ - port->writep = port->in_buffer + IN_BUFFER_SIZE/2; - - /* Throw away data? */ - if (port->readp >= port->in_buffer + IN_BUFFER_SIZE/2) - port->readp = port->in_buffer; - } - start_dma_in(port); - wake_up_interruptible(&port->in_wait_q); /* wake up the waiting process */ - } - } -} - -static void manual_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - int i; - - for (i = 0; i < NUMBER_OF_PORTS; i++) - { - sync_port* port = &ports[i]; - - if (!port->enabled) - { - continue; - } - - if (*R_IRQ_MASK1_RD & (1 << port->data_avail_bit)) /* Data received? */ - { - /* Read data */ - switch(port->ctrl_data_shadow & IO_MASK(R_SYNC_SERIAL1_CTRL, wordsize)) - { - case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit): - *port->writep++ = *(volatile char *)port->data_in; - break; - case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit): - { - int data = *(unsigned short *)port->data_in; - if (port->odd_input) - { - *port->writep |= (data & 0x0f00) >> 8; - *(port->writep + 1) = data & 0xff; - } - else - { - *port->writep = (data & 0x0ff0) >> 4; - *(port->writep + 1) = (data & 0x0f) << 4; - } - port->odd_input = !port->odd_input; - port->writep+=1; - } - break; - case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit): - *(unsigned short*)port->writep = *(volatile unsigned short *)port->data_in; - port->writep+=2; - break; - case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit): - *(unsigned int*)port->writep = *port->data_in; - port->writep+=3; - break; - case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit): - *(unsigned int*)port->writep = *port->data_in; - port->writep+=4; - break; - } - - if (port->writep > port->in_buffer + IN_BUFFER_SIZE) /* Wrap? */ - port->writep = port->in_buffer; - wake_up_interruptible(&port->in_wait_q); /* Wake up application */ - } - - if (*R_IRQ_MASK1_RD & (1 << port->transmitter_ready_bit)) /* Transmitter ready? */ - { - if (port->out_count) /* More data to send */ - send_word(port); - else /* transmission finished */ - { - *R_IRQ_MASK1_CLR = 1 << port->ready_irq_bit; /* Turn off IRQ */ - wake_up_interruptible(&port->out_wait_q); /* Wake up application */ - } - } - } -} - -module_init(etrax_sync_serial_init); diff --git a/arch/cris/drivers/usb-host.c b/arch/cris/drivers/usb-host.c deleted file mode 100644 index 3a6326f735ae..000000000000 --- a/arch/cris/drivers/usb-host.c +++ /dev/null @@ -1,2516 +0,0 @@ -/* - * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD) - * - * Copyright (c) 2001 Axis Communications AB. - * - * $Id: usb-host.c,v 1.11 2001/09/26 11:52:16 bjornw Exp $ - * - */ - -#include <linux/config.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/unistd.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/version.h> -#include <linux/list.h> - -#include <asm/uaccess.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/dma.h> -#include <asm/system.h> -#include <asm/svinto.h> - -#include <linux/usb.h> -#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.11 $"; - -#undef KERN_DEBUG -#define KERN_DEBUG "" - -#undef USB_DEBUG_RH -#undef USB_DEBUG_EP -#undef USB_DEBUG_DESC -#undef USB_DEBUG_TRACE -#undef USB_DEBUG_CTRL -#undef USB_DEBUG_BULK -#undef USB_DEBUG_INTR - -#ifdef USB_DEBUG_RH -#define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg) -#else -#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) -#else -#define dbg_ep(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_CTRL -#define dbg_ctrl(format, arg...) printk(KERN_DEBUG __FILE__ ": (CTRL) " format "\n" , ## arg) -#else -#define dbg_ctrl(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_BULK -#define dbg_bulk(format, arg...) printk(KERN_DEBUG __FILE__ ": (BULK) " format "\n" , ## arg) -#else -#define dbg_bulk(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_INTR -#define dbg_intr(format, arg...) printk(KERN_DEBUG __FILE__ ": (INTR) " format "\n" , ## arg) -#else -#define dbg_intr(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")) -#else -#define DBFENTER (NULL) -#define DBFEXIT (NULL) -#endif - -/*------------------------------------------------------------------- - Virtual Root Hub - -------------------------------------------------------------------*/ - -static __u8 root_hub_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x00, /* __u16 bcdUSB; v1.0 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; Bit 7: Bus-powered */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -static __u8 root_hub_hub_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x29, /* __u8 bDescriptorType; Hub-descriptor */ - 0x02, /* __u8 bNbrPorts; */ - 0x00, /* __u16 wHubCharacteristics; */ - 0x00, - 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ - 0x00, /* __u8 bHubContrCurrent; 0 mA */ - 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ - 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ -}; - - -#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 submit_urb_count = 0; - -//#define ETRAX_USB_INTR_IRQ -//#define ETRAX_USB_INTR_ERROR_FATAL - -#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 - -#define MAX_INTR_INTERVAL 128 - -static __u32 ep_usage_bitmask; -static __u32 ep_really_active; - -static unsigned char RxBuf[RX_BUF_SIZE]; -static USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4))); - -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))); - -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]; -static kmem_cache_t *usb_desc_cache; -static struct usb_bus *etrax_usb_bus; - -static void dump_urb (struct urb *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 void etrax_rh_int_timer_do(unsigned long ptr); - -static void etrax_usb_setup_epid(char epid, char devnum, char endpoint, - char packsize, char slow); -static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp); -static int etrax_usb_allocate_epid(void); -static void etrax_usb_free_epid(char epid); -static void cleanup_sb(USB_SB_Desc_t *sb); - -static int etrax_usb_do_ctrl_hw_add(struct urb *urb, char epid, char maxlen, int mem_flags); -static int etrax_usb_do_bulk_hw_add(struct urb *urb, char epid, char maxlen, int mem_flags); - -static int etrax_usb_submit_ctrl_urb(struct urb *urb, int mem_flags); - -static int etrax_usb_submit_urb(struct urb *urb, int mem_flags); -static int etrax_usb_unlink_urb(struct urb *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 int etrax_rh_submit_urb (struct urb *urb); - -static int etrax_usb_hc_init(void); -static void etrax_usb_hc_cleanup(void); - -static struct usb_operations etrax_usb_device_operations = -{ - etrax_usb_allocate_dev, - etrax_usb_deallocate_dev, - etrax_usb_get_frame_number, - etrax_usb_submit_urb, - 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); -} - - -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); -} - - -#else -#define dump_urb(...) (NULL) -#define dump_ep_desc(...) (NULL) -#define dump_sb_desc(...) (NULL) -#define dump_in_desc(...) (NULL) -#endif - -static void init_rx_buffers(void) -{ - int i; - - DBFENTER; - - for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) { - RxDescList[i].sw_len = RX_DESC_BUF_SIZE; - RxDescList[i].command = 0; - RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]); - RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE)); - RxDescList[i].hw_len = 0; - RxDescList[i].status = 0; - } - - RxDescList[i].sw_len = RX_DESC_BUF_SIZE; - RxDescList[i].command = IO_STATE(USB_IN_command, eol, yes); - RxDescList[i].next = virt_to_phys(&RxDescList[0]); - RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE)); - RxDescList[i].hw_len = 0; - RxDescList[i].status = 0; - - myNextRxDesc = &RxDescList[0]; - myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; - myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; - - *R_DMA_CH9_FIRST = virt_to_phys(myNextRxDesc); - *R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, start); - - DBFEXIT; -} - -static void init_tx_ctrl_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]); - } - - 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); - - DBFEXIT; -} - -static void init_tx_bulk_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]); - } - - 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].nep = virt_to_phys(&TxBulkEPList[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); - - DBFEXIT; -} - -static void init_tx_intr_ep(void) -{ - int i; - - DBFENTER; - - TxIntrSB_zout.sw_len = 0; - 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); - - for (i = 0; i < (MAX_INTR_INTERVAL - 1); 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].sub = virt_to_phys(&TxIntrSB_zout); - TxIntrEPList[i].nep = virt_to_phys(&TxIntrEPList[i + 1]); - } - - 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].sub = virt_to_phys(&TxIntrSB_zout); - TxIntrEPList[i].nep = 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) -{ - struct usb_device *usb_dev = urb->dev; - etrax_hc_t *hc = usb_dev->bus->hcpriv; - - USB_EP_Desc_t *tmp_ep; - USB_EP_Desc_t *first_ep; - - USB_EP_Desc_t *ep_desc; - USB_SB_Desc_t *sb_desc; - - char epid; - char devnum; - char endpoint; - char slow; - int maxlen; - int i; - - etrax_urb_priv_t *urb_priv; - unsigned long flags; - - 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)); - - epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen); - 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 (time_before(jiffies, i)) - ; - - first_ep = &TxIntrEPList[0]; - tmp_ep = first_ep; - - 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); - } - - tmp_ep = phys_to_virt(tmp_ep->nep); - - } while (tmp_ep != first_ep); - - /* 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); - - clear_bit(epid, (void *)&ep_really_active); - URB_List[epid] = NULL; - etrax_usb_free_epid(epid); - - DBFEXIT; - - return 0; -} - -void etrax_usb_do_intr_recover(int epid) -{ - 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); -} - -static int etrax_usb_submit_intr_urb(struct urb *urb, mem_flags) -{ - USB_EP_Desc_t *tmp_ep; - USB_EP_Desc_t *first_ep; - - USB_SB_Desc_t *sb_desc; - - char epid; - char devnum; - char endpoint; - char maxlen; - char slow; - int interval; - int i; - - etrax_urb_priv_t *urb_priv; - 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)); - - slow = usb_pipeslow(urb->pipe); - interval = urb->interval; - - 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); - 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); - } - /* Ok, now we got valid endpoint, lets insert some traffic */ - - urb_priv = (etrax_urb_priv_t *)kmalloc(sizeof(etrax_urb_priv_t), mem_flags); - 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; - - /* 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 - - /* 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; - } - - urb->interval = interval = 1 << (i - 1); - - dbg_intr("Interval rounded to %d", interval); - - 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; - - traffic_ep = (USB_EP_Desc_t *) - kmem_cache_alloc(usb_desc_cache, mem_flags); - traffic_sb = (USB_SB_Desc_t *) - kmem_cache_alloc(usb_desc_cache, mem_flags); - - 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); - } - - traffic_ep->nep = tmp_ep->nep; - tmp_ep->nep = virt_to_phys(traffic_ep); - dbg_intr("One ep sucessfully inserted"); - } - i++; - } - 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); - - DBFEXIT; - - return 0; -} - - -static int handle_intr_transfer_attn(char epid, int status) -{ - struct urb *old_urb; - - DBFENTER; - - 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; - - save_flags(flags); - cli(); - - 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; -} - -static int etrax_rh_unlink_urb (struct urb *urb) -{ - etrax_hc_t *hc; - - DBFENTER; - - hc = urb->dev->bus->hcpriv; - - if (hc->rh.urb == urb) { - hc->rh.send = 0; - del_timer(&hc->rh.rh_int_timer); - } - - DBFEXIT; - return 0; -} - -static void etrax_rh_send_irq(struct urb *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); -*/ - - data |= (hc->rh.wPortChange_1) ? (1 << 1) : 0; - data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0; - - *((__u16 *)urb->transfer_buffer) = cpu_to_le16(data); - urb->actual_length = 1; - urb->status = 0; - - - if (data && hc->rh.send && urb->complete) { - dbg_rh("wPortChange_1: 0x%04X", hc->rh.wPortChange_1); - dbg_rh("wPortChange_2: 0x%04X", hc->rh.wPortChange_2); - - urb->complete(urb); - } - -/* DBFEXIT; */ -} - -static void etrax_rh_init_int_timer(struct urb *urb) -{ - etrax_hc_t *hc; - -/* DBFENTER; */ - - hc = urb->dev->bus->hcpriv; - hc->rh.interval = urb->interval; - 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; - hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000); - add_timer(&hc->rh.rh_int_timer); - -/* DBFEXIT; */ -} - -static void etrax_rh_int_timer_do(unsigned long ptr) -{ - struct urb *urb; - etrax_hc_t *hc; - -/* DBFENTER; */ - - urb = (struct urb *)ptr; - hc = urb->dev->bus->hcpriv; - - if (hc->rh.send) { - etrax_rh_send_irq(urb); - } - - etrax_rh_init_int_timer(urb); - -/* DBFEXIT; */ -} - -static void etrax_usb_setup_epid(char epid, char devnum, char endpoint, char packsize, char slow) -{ - unsigned long flags; - - DBFENTER; - - save_flags(flags); - cli(); - - if (test_bit(epid, (void *)&ep_usage_bitmask)) { - restore_flags(flags); - - warn("Trying to setup used epid %d", epid); - DBFEXIT; - return; - } - - set_bit(epid, (void *)&ep_usage_bitmask); - dbg_ep("Setting up ep_id %d with devnum %d, endpoint %d and max_len %d", - epid, devnum, endpoint, packsize); - - *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); - - restore_flags(flags); - - DBFEXIT; -} - -static void etrax_usb_free_epid(char epid) -{ - unsigned long flags; - - DBFENTER; - - if (!test_bit(epid, (void *)&ep_usage_bitmask)) { - warn("Trying to free unused epid %d", epid); - DBFEXIT; - return; - } - - save_flags(flags); - cli(); - - *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("+"); - *R_USB_EPT_DATA = 0; - clear_bit(epid, (void *)&ep_usage_bitmask); - - restore_flags(flags); - - dbg_ep("epid: %d freed", epid); - - DBFEXIT; -} - - -static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp) -{ - int i; - unsigned long flags; - __u32 data; - - DBFENTER; - - save_flags(flags); - - /* 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)) { - *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)) { - restore_flags(flags); - - dbg_ep("Found ep_id %d for devnum %d, endpoint %d", - i, devnum, endpoint); - DBFEXIT; - return i; - } - } - } - - restore_flags(flags); - - dbg_ep("Found no ep_id for devnum %d, endpoint %d", - devnum, endpoint); - DBFEXIT; - return -1; -} - -static int etrax_usb_allocate_epid(void) -{ - int i; - - 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); - DBFEXIT; - return i; - } - } - - dbg_ep("Found no free ep_id's"); - DBFEXIT; - return -1; -} - -static int etrax_usb_submit_bulk_urb(struct urb *urb, int mem_flags) -{ - char epid; - char devnum; - char endpoint; - char maxlen; - char slow; - - struct urb *tmp_urb; - - etrax_urb_priv_t *urb_priv; - 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)); - slow = usb_pipeslow(urb->pipe); - - epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen); - 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); - } - /* Ok, now we got valid endpoint, lets insert some traffic */ - - 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_bulk_hw_add(urb, epid, maxlen, mem_flags); - } - - DBFEXIT; - - return 0; -} - -static int etrax_usb_do_bulk_hw_add(struct urb *urb, char epid, char maxlen, int mem_flags) -{ - USB_SB_Desc_t *sb_desc_1; - - etrax_urb_priv_t *urb_priv; - - unsigned long flags; - __u32 r_usb_ept_data; - - DBFENTER; - - urb_priv = kmalloc(sizeof(etrax_urb_priv_t), mem_flags); - sb_desc_1 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, mem_flags); - - if (usb_pipeout(urb->pipe)) { - - 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); - - if (urb->transfer_buffer_length > 0xffff) { - panic(__FILE__ __FUNCTION__ ":urb->transfer_buffer_length > 0xffff\n"); - } - - 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) | - -#if 0 - IO_STATE(USB_SB_command, full, no) | -#else - IO_STATE(USB_SB_command, full, yes) | -#endif - - 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; - - } 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 ? - (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; - - urb_priv->rx_offset = 0; - urb_priv->eot = 0; - } - - urb_priv->first_sb = sb_desc_1; - - urb->hcpriv = (void *)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 */ - - save_flags(flags); - cli(); - - *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__); - } - - *R_USB_EPT_DATA &= - ~(IO_MASK(R_USB_EPT_DATA, error_count_in) | - IO_MASK(R_USB_EPT_DATA, error_count_out)); - - if (usb_pipeout(urb->pipe)) { - char toggle = - 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)); - *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); - TxBulkEPList[epid].hw_len = 0; - TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - - restore_flags(flags); - - 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); - - } - - DBFEXIT; -} - -static int handle_bulk_transfer_attn(char epid, int status) -{ - struct urb *old_urb; - etrax_urb_priv_t *hc_priv; - unsigned long flags; - - 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)) { - 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; - } 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); - } - - restore_flags(flags); - } - - save_flags(flags); - cli(); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop(); - if (usb_pipeout(old_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); - } 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); - } - 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)), - GFP_KERNEL); - } -#if 1 - else { - /* 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); - - old_urb->status = status; - if (old_urb->complete) { - old_urb->complete(old_urb); - } - - DBFEXIT; -} - -/* ---------------------------------------------------------------------------- */ - -static int etrax_usb_submit_ctrl_urb(struct urb *urb, int mem_flags) -{ - char epid; - char devnum; - char endpoint; - char maxlen; - char slow; - - struct urb *tmp_urb; - - etrax_urb_priv_t *urb_priv; - 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)); - slow = usb_pipeslow(urb->pipe); - - epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen); - 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); - } - /* Ok, now we got valid endpoint, lets insert some traffic */ - - 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, mem_flags); - } - - DBFEXIT; - - return 0; -} - -static int etrax_usb_do_ctrl_hw_add(struct urb *urb, char epid, char maxlen, int mem_flags) -{ - USB_SB_Desc_t *sb_desc_1; - USB_SB_Desc_t *sb_desc_2; - USB_SB_Desc_t *sb_desc_3; - - etrax_urb_priv_t *urb_priv; - - unsigned long flags; - __u32 r_usb_ept_data; - - - DBFENTER; - - urb_priv = kmalloc(sizeof(etrax_urb_priv_t), mem_flags); - sb_desc_1 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, mem_flags); - sb_desc_2 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, mem_flags); - - 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); - - if (usb_pipeout(urb->pipe)) { - 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 */ - - 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, mem_flags); - - sb_desc_1->next = virt_to_phys(sb_desc_3); - - 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); - - } 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, mem_flags); - - sb_desc_2->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); - - 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_3->buf = 0; - sb_desc_3->next = 0; - dump_sb_desc(sb_desc_3); - - urb_priv->rx_offset = 0; - urb_priv->eot = 0; - } - - urb_priv->first_sb = sb_desc_1; - - urb->hcpriv = (void *)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 */ - - save_flags(flags); - cli(); - - *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__); - } - - - *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. */ - - set_bit(epid, (void *)&ep_really_active); - - TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_1); - 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); - - } - - DBFEXIT; -} - -static int etrax_usb_submit_urb(struct urb *urb, int mem_flags) -{ - etrax_hc_t *hc; - int rval = -EINVAL; - - 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, mem_flags); - - } else if (usb_pipetype(urb->pipe) == PIPE_BULK) { - rval = etrax_usb_submit_bulk_urb(urb, mem_flags); - - } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { - int bustime; - - 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, mem_flags); - } - - } - } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - warn("Isochronous traffic is not supported !!!"); - rval = -EINVAL; - } - - DBFEXIT; - - return rval; -} - -static int etrax_usb_unlink_urb(struct urb *urb) -{ - etrax_hc_t *hc = urb->dev->bus->hcpriv; - int epid; - int pos; - int devnum, endpoint, slow, maxlen; - 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)); - - epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen); - - 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; - } - - info("Unlink of BULK or CTRL"); - - save_flags(flags); - cli(); - - for (epid = 0; epid < 32; epid++) { - struct urb *u = URB_List[epid]; - pos = 0; - - for (; u; u = u->next) { - pos++; - if (u == urb) { - info("Found urb at epid %d, pos %d", epid, pos); - - if (pos == 1) { - 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])); - } - } - - URB_List[epid] = u->next; - - } else { - struct urb *up; - for (up = URB_List[epid]; up->next != u; up = up->next); - up->next = u->next; - } - 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); - } - } - } - - restore_flags(flags); - - DBFEXIT; - return 0; -} - -static int etrax_usb_get_frame_number(struct usb_device *usb_dev) -{ - DBFENTER; - DBFEXIT; - return (*R_USB_FM_NUMBER); -} - -static int etrax_usb_allocate_dev(struct usb_device *usb_dev) -{ - DBFENTER; - DBFEXIT; - return 0; -} - -static int etrax_usb_deallocate_dev(struct usb_device *usb_dev) -{ - DBFENTER; - DBFEXIT; - return 0; -} - -static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs) -{ - etrax_hc_t *hc = (etrax_hc_t *)vhc; - int epid; - char eol; - struct urb *urb; - USB_EP_Desc_t *tmp_ep; - USB_SB_Desc_t *tmp_sb; - - DBFENTER; - - 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); - } - - DBFEXIT; -} - -static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs) -{ - int epid = 0; - struct urb *urb; - etrax_urb_priv_t *urb_priv; - - *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do); - - while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) { - if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) { - - goto skip_out; - } - - if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) { - - goto skip_out; - } - - epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status); - - urb = URB_List[epid]; - - if (urb && usb_pipein(urb->pipe)) { - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - - 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 was bigger " - "than the URB has room for !!!", epid); - goto skip_out; - } - - 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)) { - urb_priv->eot = 1; - } - - } else { - err("This is almost fatal, inpacket for epid %d which does not exist " - " or is out !!!\nURB was at 0x%08X", epid, urb); - - goto skip_out; - } - - 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); - } -} - - - -static void cleanup_sb(USB_SB_Desc_t *sb) -{ - USB_SB_Desc_t *next_sb; - - DBFENTER; - - if (sb == NULL) { - err("cleanup_sb was given a NULL pointer"); - return; - } - - 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; - } - - kmem_cache_free(usb_desc_cache, sb); - - DBFEXIT; - -} - -static int handle_control_transfer_attn(char epid, int status) -{ - struct urb *old_urb; - etrax_urb_priv_t *hc_priv; - - 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)), - GFP_KERNEL); - } -#if 1 - else { - /* 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); - - old_urb->status = status; - if (old_urb->complete) { - old_urb->complete(old_urb); - } - - DBFEXIT; -} - - - -static void etrax_usb_hc_intr_bottom_half(void *data) -{ - struct usb_reg_context *reg = (struct usb_reg_context *)data; - struct urb *old_urb; - - int error_code; - int epid; - - __u32 r_usb_ept_data; - - etrax_hc_t *hc = reg->hc; - __u16 r_usb_rh_port_status_1; - __u16 r_usb_rh_port_status_2; - - DBFENTER; - - if (reg->r_usb_irq_mask_read & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) { - - /* - 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. - */ - - 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; - - 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); - - /* 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 */ - 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; - } - - for (epid = 0; epid < 32; epid++) { - - unsigned long flags; - - 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 (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) { - warn("Was hold for epid %d", epid); - continue; - } - - if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) { - continue; - } - - - if (test_bit(epid, (void *)®->r_usb_epid_attn)) { - - if (URB_List[epid] == NULL) { - err("R_USB_EPT_DATA is 0x%08X", r_usb_ept_data); - err("submit urb has been called %d 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 endpoint %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)); - - - } - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) { - warn("Stall for endpoint %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 endpoint %d\n", epid); - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) { - warn("Buffer error for endpoint %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 (test_bit(epid, (void *)&ep_really_active)) { - /* Should really be else if (testbit(really active)) */ - - if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) { - - 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); - } - } - - } - - kfree(reg); - - DBFEXIT; -} - - -static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs) -{ - struct usb_reg_context *reg; - - DBFENTER; - - reg = (struct usb_reg_context *)kmalloc(sizeof(struct usb_reg_context), GFP_ATOMIC); - - if (!(reg)) { - panic("kmalloc failed in top_half\n"); - } - - 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; - - 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; - - reg->usb_bh.sync = 0; - reg->usb_bh.routine = etrax_usb_hc_intr_bottom_half; - reg->usb_bh.data = reg; - - queue_task(®->usb_bh, &tq_immediate); - mark_bh(IMMEDIATE_BH); - - DBFEXIT; -} - -static int etrax_rh_submit_urb(struct urb *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; - void *data = urb->transfer_buffer; - int leni = urb->transfer_buffer_length; - int len = 0; - int status = 0; - int stat = 0; - int i; - - __u16 cstatus; - - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - - DBFENTER; - - 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; - hc->rh.interval = urb->interval; - etrax_rh_init_int_timer(urb); - DBFEXIT; - - 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); - - switch (bmRType_bReq) { - - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *) data = cpu_to_le16 (1); - OK (2); - - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data = cpu_to_le16 (0); - OK (2); - - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data = cpu_to_le16 (0); - OK (2); - - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data = cpu_to_le32 (0); - OK (4); /* hub power ** */ - - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - 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) { - *((__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 !!"); - OK(0); - } - - OK(4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): - OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case (RH_C_HUB_OVER_CURRENT): - OK (0); /* hub power over current ** */ - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): - if (wIndex == 1) { - - dbg_rh("trying to do disable of 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); - dbg_rh("Port 1 is disabled"); - - } else if (wIndex == 2) { - - dbg_rh("trying to do disable of 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); - dbg_rh("Port 2 is disabled"); - - } else { - dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE " - "with invalid wIndex == %d!!", wIndex); - } - - OK (0); - case (RH_PORT_SUSPEND): - /* Opposite to suspend should be resume, so well do a resume */ - 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, 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, ctrl_cmd, nop); - } else { - dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND " - "with invalid wIndex == %d!!", wIndex); - } - - OK (0); - case (RH_PORT_POWER): - OK (0); /* port power ** */ - case (RH_C_PORT_CONNECTION): - - if (wIndex == 1) { - hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION); - } - else if (wIndex == 2) { - hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION); - } - else { - dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION " - "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) { - hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE); - } - else { - dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE " - "with invalid wIndex == %d!!", wIndex); - } - OK (0); - case (RH_C_PORT_SUSPEND): -/*** WR_RH_PORTSTAT(RH_PS_PSSC); */ - OK (0); - case (RH_C_PORT_OVER_CURRENT): - OK (0); /* port power over current ** */ - 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); - - 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); - } - - OK (0); - - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - if (wIndex == 1) { - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port1) | - IO_STATE(R_USB_COMMAND, port_cmd, suspend) | - 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, suspend) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - } else { - dbg_rh("RH_SET_FEATURE->RH_C_PORT_SUSPEND " - "with invalid wIndex == %d!!", wIndex); - } - - OK (0); - case (RH_PORT_RESET): - if (wIndex == 1) { - int port1_retry; - - port1_redo: - dbg_rh("Doing reset of port 1"); - - *R_USB_COMMAND = - 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 */ - - 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 ?? */ - - udelay(15000); - } - else if (wIndex == 2) { - int port2_retry; - - port2_redo: - dbg_rh("Doing reset of port 2"); - - *R_USB_COMMAND = - 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 */ - - 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 ?? */ - - udelay(15000); - } - - /* 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!!!! */ - OK (0); - - } - break; - - case RH_SET_ADDRESS: - hc->rh.devnum = wValue; - dbg_rh("RH address set to: %d", hc->rh.devnum); - OK (0); - - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_dev_des), wLength)); - memcpy (data, root_hub_dev_des, len); - OK (len); - case (0x02): /* configuration descriptor */ - len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_config_des), wLength)); - memcpy (data, root_hub_config_des, len); - OK (len); - case (0x03): /* string descriptors */ - len = usb_root_hub_string (wValue & 0xff, - 0xff, "ETRAX 100LX", - data, wLength); - if (len > 0) { - OK(min_t(int, leni, len)); - } else - stat = -EPIPE; - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - root_hub_hub_des[2] = hc->rh.numports; - len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_hub_des), wLength)); - memcpy (data, root_hub_hub_des, len); - OK (len); - - case RH_GET_CONFIGURATION: - *(__u8 *) data = 0x01; - OK (1); - - case RH_SET_CONFIGURATION: - OK (0); - - default: - stat = -EPIPE; - } - - urb->actual_length = len; - urb->status = stat; - urb->dev=NULL; - if (urb->complete) { - urb->complete (urb); - } - DBFEXIT; - - return 0; -} - -static int __init etrax_usb_hc_init(void) -{ - static etrax_hc_t *hc; - struct usb_bus *bus; - struct usb_device *usb_rh; - - DBFENTER; - - info("ETRAX 100LX USB-HCD %s (c) 2001 Axis Communications AB\n", usb_hcd_version); - - hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL); - - /* 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"); - } - - etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations); - hc->bus = bus; - bus->hcpriv = hc; - - /* Initalize RH to the default address. - And make sure that we have no status change indication */ - hc->rh.numports = 2; /* The RH has two ports */ - hc->rh.devnum = 0; - hc->rh.wPortChange_1 = 0; - hc->rh.wPortChange_2 = 0; - - /* Also initate the previous values to zero */ - hc->rh.prev_wPortStatus_1 = 0; - hc->rh.prev_wPortStatus_2 = 0; - - /* Initialize the intr-traffic flags */ - 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; - - memset(URB_List, 0, sizeof(URB_List)); - - /* This code should really be moved */ - - if (request_dma(USB_TX_DMA_NBR, "ETRAX 100LX built-in USB (Tx)")) { - err("Could not allocate DMA ch 8 for USB"); - etrax_usb_hc_cleanup(); - DBFEXIT; - return -1; - } - - if (request_dma(USB_RX_DMA_NBR, "ETRAX 100LX built-in USB (Rx)")) { - err("Could not allocate DMA ch 9 for USB"); - 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 - - usb_register_bus(hc->bus); - - /* We may have to set more bits, but these are the obvious ones */ - *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); - - *R_IRQ_MASK2_SET = - IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) | - IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set); - - *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, 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); - - if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_intr_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(); - DBFEXIT; - return -1; - } - - if (request_irq(ETRAX_USB_RX_IRQ, etrax_usb_rx_interrupt, 0, - "ETRAX 100LX built-in USB (Rx)", hc)) { - err("Could not allocate IRQ %d for USB", ETRAX_USB_RX_IRQ); - etrax_usb_hc_cleanup(); - DBFEXIT; - return -1; - } - - if (request_irq(ETRAX_USB_TX_IRQ, etrax_usb_tx_interrupt, 0, - "ETRAX 100LX built-in USB (Tx)", hc)) { - err("Could not allocate IRQ %d for USB", ETRAX_USB_TX_IRQ); - etrax_usb_hc_cleanup(); - DBFEXIT; - return -1; - } - - /* Reset the USB interface (configures as HC) */ - *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 - -#ifdef CONFIG_ETRAX_USB_HOST_PORT1 - *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no); -#endif - -#ifdef CONFIG_ETRAX_USB_HOST_PORT2 - *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)); - - *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)); - - /* Here we must wait at least 10ms so the device has time to recover */ - udelay(15000); - - 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 */ - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run); - - nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - usb_rh = usb_alloc_dev(NULL, hc->bus); - hc->bus->root_hub = usb_rh; - usb_connect(usb_rh); - usb_new_device(usb_rh); - - DBFEXIT; - - return 0; -} - -static void etrax_usb_hc_cleanup(void) -{ - DBFENTER; - - free_irq(ETRAX_USB_HC_IRQ, NULL); - free_irq(ETRAX_USB_RX_IRQ, NULL); - free_irq(ETRAX_USB_TX_IRQ, NULL); - - free_dma(USB_TX_DMA_NBR); - free_dma(USB_RX_DMA_NBR); - usb_deregister_bus(etrax_usb_bus); - - DBFEXIT; -} - -module_init(etrax_usb_hc_init); -module_exit(etrax_usb_hc_cleanup); diff --git a/arch/cris/drivers/usb-host.h b/arch/cris/drivers/usb-host.h deleted file mode 100644 index 434d50b26021..000000000000 --- a/arch/cris/drivers/usb-host.h +++ /dev/null @@ -1,243 +0,0 @@ -#ifndef __LINUX_ETRAX_USB_H -#define __LINUX_ETRAX_USB_H - -#include <linux/types.h> -#include <linux/list.h> - -typedef struct USB_IN_Desc { - __u16 sw_len; - __u16 command; - unsigned long next; - unsigned long buf; - __u16 hw_len; - __u16 status; -} USB_IN_Desc_t; - -typedef struct USB_SB_Desc { - __u16 sw_len; - __u16 command; - unsigned long next; - 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; - __u32 dummy; -} USB_EP_Desc_t; - -struct virt_root_hub { - int devnum; - void *urb; - void *int_addr; - int send; - int interval; - int numports; - struct timer_list rh_int_timer; - __u16 wPortChange_1; - __u16 wPortChange_2; - __u16 prev_wPortStatus_1; - __u16 prev_wPortStatus_2; -}; - -struct etrax_usb_intr_traffic { - int sleeping; - int error; - struct wait_queue *wq; -}; - -typedef struct etrax_usb_hc { - struct usb_bus *bus; - struct virt_root_hub rh; - struct etrax_usb_intr_traffic intr; -} etrax_hc_t; - -typedef enum {idle, eot, nodata} etrax_usb_rx_state_t; - -typedef struct etrax_usb_urb_priv { - USB_SB_Desc_t *first_sb; - __u32 rx_offset; - etrax_usb_rx_state_t rx_state; - __u8 eot; - struct list_head ep_in_list; -} etrax_urb_priv_t; - - -struct usb_reg_context -{ - 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; - __u32 r_usb_irq_mask_read; - struct tq_struct usb_bh; -#if 0 - __u32 r_usb_ept_data[32]; -#endif -}; - -struct in_chunk -{ - void *data; - int length; - char epid; - struct list_head list; -}; - - -/* --------------------------------------------------------------------------- - Virtual Root HUB - ------------------------------------------------------------------------- */ -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -/* Our Vendor Specific feature */ -#define RH_REMOVE_EP 0x00 - - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 - -/* Field definitions for */ - -#define USB_IN_command__eol__BITNR 0 /* command macros */ -#define USB_IN_command__eol__WIDTH 1 -#define USB_IN_command__eol__no 0 -#define USB_IN_command__eol__yes 1 - -#define USB_IN_command__intr__BITNR 3 -#define USB_IN_command__intr__WIDTH 1 -#define USB_IN_command__intr__no 0 -#define USB_IN_command__intr__yes 1 - -#define USB_IN_status__eop__BITNR 1 /* status macros. */ -#define USB_IN_status__eop__WIDTH 1 -#define USB_IN_status__eop__no 0 -#define USB_IN_status__eop__yes 1 - -#define USB_IN_status__eot__BITNR 5 -#define USB_IN_status__eot__WIDTH 1 -#define USB_IN_status__eot__no 0 -#define USB_IN_status__eot__yes 1 - -#define USB_IN_status__error__BITNR 6 -#define USB_IN_status__error__WIDTH 1 -#define USB_IN_status__error__no 0 -#define USB_IN_status__error__yes 1 - -#define USB_IN_status__nodata__BITNR 7 -#define USB_IN_status__nodata__WIDTH 1 -#define USB_IN_status__nodata__no 0 -#define USB_IN_status__nodata__yes 1 - -#define USB_IN_status__epid__BITNR 8 -#define USB_IN_status__epid__WIDTH 5 - -#define USB_EP_command__eol__BITNR 0 -#define USB_EP_command__eol__WIDTH 1 -#define USB_EP_command__eol__no 0 -#define USB_EP_command__eol__yes 1 - -#define USB_EP_command__eof__BITNR 1 -#define USB_EP_command__eof__WIDTH 1 -#define USB_EP_command__eof__no 0 -#define USB_EP_command__eof__yes 1 - -#define USB_EP_command__intr__BITNR 3 -#define USB_EP_command__intr__WIDTH 1 -#define USB_EP_command__intr__no 0 -#define USB_EP_command__intr__yes 1 - -#define USB_EP_command__enable__BITNR 4 -#define USB_EP_command__enable__WIDTH 1 -#define USB_EP_command__enable__no 0 -#define USB_EP_command__enable__yes 1 - -#define USB_EP_command__hw_valid__BITNR 5 -#define USB_EP_command__hw_valid__WIDTH 1 -#define USB_EP_command__hw_valid__no 0 -#define USB_EP_command__hw_valid__yes 1 - -#define USB_EP_command__epid__BITNR 8 -#define USB_EP_command__epid__WIDTH 5 - -#define USB_SB_command__eol__BITNR 0 /* command macros. */ -#define USB_SB_command__eol__WIDTH 1 -#define USB_SB_command__eol__no 0 -#define USB_SB_command__eol__yes 1 - -#define USB_SB_command__eot__BITNR 1 -#define USB_SB_command__eot__WIDTH 1 -#define USB_SB_command__eot__no 0 -#define USB_SB_command__eot__yes 1 - -#define USB_SB_command__intr__BITNR 3 -#define USB_SB_command__intr__WIDTH 1 -#define USB_SB_command__intr__no 0 -#define USB_SB_command__intr__yes 1 - -#define USB_SB_command__tt__BITNR 4 -#define USB_SB_command__tt__WIDTH 2 -#define USB_SB_command__tt__zout 0 -#define USB_SB_command__tt__in 1 -#define USB_SB_command__tt__out 2 -#define USB_SB_command__tt__setup 3 - - -#define USB_SB_command__rem__BITNR 8 -#define USB_SB_command__rem__WIDTH 6 - -#define USB_SB_command__full__BITNR 6 -#define USB_SB_command__full__WIDTH 1 -#define USB_SB_command__full__no 0 -#define USB_SB_command__full__yes 1 - -#endif diff --git a/arch/cris/kernel/Makefile b/arch/cris/kernel/Makefile index a340c8fb59fa..7f6d9b43cf1f 100644 --- a/arch/cris/kernel/Makefile +++ b/arch/cris/kernel/Makefile @@ -1,21 +1,13 @@ -# $Id: Makefile,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +# $Id: Makefile,v 1.8 2003/04/09 05:20:47 starvik Exp $ # # Makefile for the linux kernel. # -extra-y := head.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-y := process.o traps.o irq.o ptrace.o setup.o \ + time.o sys_cris.o semaphore.o obj-$(CONFIG_MODULES) += ksyms.o -obj-$(CONFIG_ETRAX_KGDB) += kgdb.o - -# This dependency isn't caught by mkdep. See entry.S. -entry.o: entryoffsets.s - -entryoffsets.s: entryoffsets.c - $(CC) $(CFLAGS) -S -c $< +obj-$(CONFIG_MODULES) += module.o clean: + diff --git a/arch/cris/kernel/entryoffsets.c b/arch/cris/kernel/entryoffsets.c deleted file mode 100644 index f1480acd1047..000000000000 --- a/arch/cris/kernel/entryoffsets.c +++ /dev/null @@ -1,62 +0,0 @@ -/* linux/arch/cris/entryoffsets.c - * - * Copyright (C) 2001 Axis Communications AB - * - * Generate structure offsets for use in entry.S. No extra processing - * needed more than compiling this file to assembly code. Horrendous - * assembly code will be generated, so don't look at that. - * - * Authors: Hans-Peter Nilsson (hp@axis.com) - */ - -/* There can be string constants fallout from inline functions, so we'd - better make sure we don't assemble anything emitted from inclusions. */ -__asm__ (".if 0"); - -#include <linux/sched.h> -#include <linux/ptrace.h> -#include <asm/processor.h> - -/* Exclude everything except the assembly by wrapping it in ".if 0". */ -#undef VAL -#define VAL(NAME, VALUE) \ -void NAME ## _fun (void) \ - { \ - __asm__ (".endif \n" \ - #NAME " = %0 \n" \ - ".if 0\n" \ - : : "i" (VALUE)); \ - } - -#undef OF -#define OF(NAME, TYPE, MEMBER) \ - VAL (NAME, offsetof (TYPE, MEMBER)) - -/* task_struct offsets. */ -#error OF (LTASK_SIGPENDING, struct task_struct, sigpending) -#error OF (LTASK_NEEDRESCHED, struct task_struct, need_resched) -#error OF (LTASK_PTRACE, struct task_struct, ptrace) -OF (LTASK_PID, struct task_struct, pid) - -/* pt_regs offsets. */ -OF (LORIG_R10, struct pt_regs, orig_r10) -OF (LR13, struct pt_regs, r13) -OF (LR12, struct pt_regs, r12) -OF (LR11, struct pt_regs, r11) -OF (LR10, struct pt_regs, r10) -OF (LR9, struct pt_regs, r9) -OF (LMOF, struct pt_regs, mof) -OF (LDCCR, struct pt_regs, dccr) -OF (LSRP, struct pt_regs, srp) -OF (LIRP, struct pt_regs, irp) - -/* thread_struct offsets. */ -OF (LTHREAD_KSP, struct thread_struct, ksp) -OF (LTHREAD_USP, struct thread_struct, usp) -OF (LTHREAD_DCCR, struct thread_struct, dccr) - -/* linux/sched.h values - doesn't have an #ifdef __ASSEMBLY__ for these. */ -VAL (LCLONE_VM, CLONE_VM) -VAL (LCLONE_UNTRACED, CLONE_UNTRACED) - -__asm__ (".endif"); diff --git a/arch/cris/kernel/irq.c b/arch/cris/kernel/irq.c index c63a5f02b00d..934f8715b5af 100644 --- a/arch/cris/kernel/irq.c +++ b/arch/cris/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $ +/* $Id: irq.c,v 1.8 2003/07/04 08:27:52 starvik Exp $ * * linux/arch/cris/kernel/irq.c * @@ -23,7 +23,7 @@ #include <linux/config.h> #include <linux/ptrace.h> -#include <linux/errno.h> + #include <linux/kernel_stat.h> #include <linux/signal.h> #include <linux/sched.h> @@ -34,47 +34,34 @@ #include <linux/random.h> #include <linux/init.h> #include <linux/seq_file.h> +#include <linux/errno.h> -#include <asm/system.h> #include <asm/io.h> -#include <asm/irq.h> #include <asm/bitops.h> -#include <asm/svinto.h> - -char *hw_bp_msg = "BP 0x%x\n"; - -static inline void -mask_irq(unsigned int irq_nr) -{ - *R_VECT_MASK_CLR = 1 << irq_nr; -} - -static inline void -unmask_irq(unsigned int irq_nr) -{ - *R_VECT_MASK_SET = 1 << irq_nr; -} +/* Defined in arch specific irq.c */ +extern void arch_setup_irq(int irq); +extern void arch_free_irq(int irq); void disable_irq(unsigned int irq_nr) { unsigned long flags; - save_flags(flags); - cli(); + local_save_flags(flags); + local_irq_disable(); mask_irq(irq_nr); - restore_flags(flags); + local_irq_restore(flags); } void enable_irq(unsigned int irq_nr) { unsigned long flags; - save_flags(flags); - cli(); + local_save_flags(flags); + local_irq_disable(); unmask_irq(irq_nr); - restore_flags(flags); + local_irq_restore(flags); } unsigned long @@ -89,140 +76,11 @@ probe_irq_off(unsigned long x) return 0; } -irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */ - -/* don't use set_int_vector, it bypasses the linux interrupt handlers. it is - * global just so that the kernel gdb can use it. - */ - -void -set_int_vector(int n, irqvectptr addr, irqvectptr saddr) -{ - /* remember the shortcut entry point, after the prologue */ - - irq_shortcuts[n] = saddr; - - etrax_irv->v[n + 0x20] = (irqvectptr)addr; -} - -/* the breakpoint vector is obviously not made just like the normal irq handlers - * but needs to contain _code_ to jump to addr. - * - * the BREAK n instruction jumps to IBR + n * 8 - */ - -void -set_break_vector(int n, irqvectptr addr) -{ - unsigned short *jinstr = (unsigned short *)&etrax_irv->v[n*2]; - unsigned long *jaddr = (unsigned long *)(jinstr + 1); - - /* if you don't know what this does, do not touch it! */ - - *jinstr = 0x0d3f; - *jaddr = (unsigned long)addr; - - /* 00000026 <clrlop+1a> 3f0d82000000 jump 0x82 */ -} - - -/* - * This builds up the IRQ handler stubs using some ugly macros in irq.h - * - * These macros create the low-level assembly IRQ routines that do all - * the operations that are needed. They are also written to be fast - and to - * disable interrupts as little as humanly possible. - * - */ - -/* IRQ0 and 1 are special traps */ -void hwbreakpoint(void); -void IRQ1_interrupt(void); -BUILD_TIMER_IRQ(2, 0x04) /* the timer interrupt is somewhat special */ -BUILD_IRQ(3, 0x08) -BUILD_IRQ(4, 0x10) -BUILD_IRQ(5, 0x20) -BUILD_IRQ(6, 0x40) -BUILD_IRQ(7, 0x80) -BUILD_IRQ(8, 0x100) -BUILD_IRQ(9, 0x200) -BUILD_IRQ(10, 0x400) -BUILD_IRQ(11, 0x800) -BUILD_IRQ(12, 0x1000) -BUILD_IRQ(13, 0x2000) -void mmu_bus_fault(void); /* IRQ 14 is the bus fault interrupt */ -void multiple_interrupt(void); /* IRQ 15 is the multiple IRQ interrupt */ -BUILD_IRQ(16, 0x10000) -BUILD_IRQ(17, 0x20000) -BUILD_IRQ(18, 0x40000) -BUILD_IRQ(19, 0x80000) -BUILD_IRQ(20, 0x100000) -BUILD_IRQ(21, 0x200000) -BUILD_IRQ(22, 0x400000) -BUILD_IRQ(23, 0x800000) -BUILD_IRQ(24, 0x1000000) -BUILD_IRQ(25, 0x2000000) -/* IRQ 26-30 are reserved */ -BUILD_IRQ(31, 0x80000000) - -/* - * Pointers to the low-level handlers - */ - -static void (*interrupt[NR_IRQS])(void) = { - NULL, NULL, IRQ2_interrupt, IRQ3_interrupt, - IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt, - IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt, - IRQ12_interrupt, IRQ13_interrupt, NULL, NULL, - IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt, - IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt, - IRQ24_interrupt, IRQ25_interrupt, NULL, NULL, NULL, NULL, NULL, - IRQ31_interrupt -}; - -static void (*sinterrupt[NR_IRQS])(void) = { - 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, - sIRQ24_interrupt, sIRQ25_interrupt, NULL, NULL, NULL, NULL, NULL, - sIRQ31_interrupt -}; - -static void (*bad_interrupt[NR_IRQS])(void) = { - NULL, NULL, - NULL, bad_IRQ3_interrupt, - bad_IRQ4_interrupt, bad_IRQ5_interrupt, - bad_IRQ6_interrupt, bad_IRQ7_interrupt, - bad_IRQ8_interrupt, bad_IRQ9_interrupt, - bad_IRQ10_interrupt, bad_IRQ11_interrupt, - bad_IRQ12_interrupt, bad_IRQ13_interrupt, - NULL, NULL, - bad_IRQ16_interrupt, bad_IRQ17_interrupt, - bad_IRQ18_interrupt, bad_IRQ19_interrupt, - bad_IRQ20_interrupt, bad_IRQ21_interrupt, - bad_IRQ22_interrupt, bad_IRQ23_interrupt, - bad_IRQ24_interrupt, bad_IRQ25_interrupt, - NULL, NULL, NULL, NULL, NULL, - bad_IRQ31_interrupt -}; - /* * Initial irq handlers. */ -static struct irqaction *irq_action[NR_IRQS] = { - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL -}; +static struct irqaction *irq_action[NR_IRQS]; int show_interrupts(struct seq_file *p, void *v) { @@ -262,9 +120,10 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) { struct irqaction *action; int do_random, cpu; + int retval = 0; cpu = smp_processor_id(); - irq_enter(cpu); + irq_enter(); kstat_cpu(cpu).irqs[irq]++; action = irq_action[irq]; @@ -275,14 +134,24 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) do_random = 0; do { do_random |= action->flags; - action->handler(irq, action->dev_id, regs); + retval |= action->handler(irq, action->dev_id, regs); action = action->next; } while (action); + + if (retval != 1) { + if (retval) { + printk("irq event %d: bogus retval mask %x\n", + irq, retval); + } else { + printk("irq %d: nobody cared\n", irq); + } + } + if (do_random & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); - local_irq_disable(); + local_irq_disable(); } - irq_exit(cpu); + irq_exit(); if (softirq_pending(cpu)) do_softirq(); @@ -295,7 +164,7 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) handler is entered into the interrupt vector */ -int setup_etrax_irq(int irq, struct irqaction * new) +int setup_irq(int irq, struct irqaction * new) { int shared = 0; struct irqaction *old, **p; @@ -322,19 +191,19 @@ int setup_etrax_irq(int irq, struct irqaction * new) if (new->flags & SA_SAMPLE_RANDOM) rand_initialize_irq(irq); - save_flags(flags); - cli(); + local_save_flags(flags); + local_irq_disable(); *p = new; if (!shared) { /* 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]); + arch_setup_irq(irq); unmask_irq(irq); } - restore_flags(flags); + local_irq_restore(flags); return 0; } @@ -348,7 +217,7 @@ int setup_etrax_irq(int irq, struct irqaction * new) */ int request_irq(unsigned int irq, - void (*handler)(int, void *, struct pt_regs *), + irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id) @@ -378,7 +247,7 @@ int request_irq(unsigned int irq, action->next = NULL; action->dev_id = dev_id; - retval = setup_etrax_irq(irq, action); + retval = setup_irq(irq, action); if (retval) kfree(action); @@ -399,14 +268,14 @@ void free_irq(unsigned int irq, void *dev_id) continue; /* Found it - now free it */ - save_flags(flags); - cli(); + local_save_flags(flags); + local_irq_disable(); *p = action->next; if (!irq_action[irq]) { mask_irq(irq); - set_int_vector(irq, bad_interrupt[irq], 0); + arch_free_irq(irq); } - restore_flags(flags); + local_irq_restore(flags); kfree(action); return; } @@ -415,84 +284,11 @@ void free_irq(unsigned int irq, void *dev_id) void weird_irq(void) { - __asm__("di"); + local_irq_disable(); printk("weird irq\n"); 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. -*/ - -void system_call(void); /* from entry.S */ -void do_sigtrap(void); /* from entry.S */ -void gdb_handle_breakpoint(void); /* from entry.S */ - -void __init -init_IRQ(void) -{ - int i; - - /* clear all interrupt masks */ - -#ifndef CONFIG_SVINTO_SIM - *R_IRQ_MASK0_CLR = 0xffffffff; - *R_IRQ_MASK1_CLR = 0xffffffff; - *R_IRQ_MASK2_CLR = 0xffffffff; -#endif - - *R_VECT_MASK_CLR = 0xffffffff; - - /* clear the shortcut entry points */ - - for(i = 0; i < NR_IRQS; i++) - irq_shortcuts[i] = NULL; - - for (i = 0; i < 256; i++) - etrax_irv->v[i] = weird_irq; - - /* the entries in the break vector contain actual code to be - executed by the associated break handler, rather than just a jump - address. therefore we need to setup a default breakpoint handler - for all breakpoints */ - - for (i = 0; i < 16; i++) - set_break_vector(i, do_sigtrap); - - /* set all etrax irq's to the bad handlers */ - for (i = 2; i < NR_IRQS; i++) - set_int_vector(i, bad_interrupt[i], 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); - set_int_vector(1, IRQ1_interrupt, 0); - - /* and irq 14 which is the mmu bus fault handler */ - - set_int_vector(14, mmu_bus_fault, 0); - - /* setup the system-call trap, which is reached by BREAK 13 */ - - set_break_vector(13, system_call); - - /* setup a breakpoint handler for debugging used for both user and - kernel mode debugging (which is why it is not inside an ifdef - CONFIG_ETRAX_KGDB) */ - set_break_vector(8, gdb_handle_breakpoint); - -#ifdef CONFIG_ETRAX_KGDB - /* setup kgdb if its enabled, and break into the debugger */ - kgdb_init(); - breakpoint(); -#endif - -} - #if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL) /* Used by other archs to show/control IRQ steering during SMP */ void __init diff --git a/arch/cris/kernel/ksyms.c b/arch/cris/kernel/ksyms.c index 4d2cbc18c6a8..1161a2525254 100644 --- a/arch/cris/kernel/ksyms.c +++ b/arch/cris/kernel/ksyms.c @@ -24,6 +24,9 @@ extern void dump_thread(struct pt_regs *, struct user *); extern unsigned long get_cmos_time(void); extern void __Udiv(void); +extern void __Umod(void); +extern void __Div(void); +extern void __Mod(void); extern void __ashrdi3(void); extern void iounmap(void *addr); @@ -44,11 +47,16 @@ 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(__Udiv); +EXPORT_SYMBOL(__Umod); +EXPORT_SYMBOL(__Div); +EXPORT_SYMBOL(__Mod); EXPORT_SYMBOL(__ashrdi3); /* Memory functions */ @@ -58,6 +66,8 @@ 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); @@ -69,14 +79,13 @@ EXPORT_SYMBOL(port_pb_config_shadow); EXPORT_SYMBOL(port_g_data_shadow); /* Userspace access functions */ -EXPORT_SYMBOL(strncpy_from_user); -EXPORT_SYMBOL(__strncpy_from_user); -EXPORT_SYMBOL(__generic_copy_from_user); -EXPORT_SYMBOL(__generic_copy_to_user); -EXPORT_SYMBOL(strnlen_user); 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); diff --git a/arch/cris/kernel/module.c b/arch/cris/kernel/module.c new file mode 100644 index 000000000000..052c0031fa29 --- /dev/null +++ b/arch/cris/kernel/module.c @@ -0,0 +1,106 @@ +/* Kernel module help for i386. + Copyright (C) 2001 Rusty Russell. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt , ...) +#endif + +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc(size); +} + + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + table entries. */ +} + +/* We don't need anything special. */ +int module_frob_arch_sections(Elf_Ehdr *hdr, + Elf_Shdr *sechdrs, + char *secstrings, + struct module *mod) +{ + return 0; +} + +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i; + Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr; + Elf32_Sym *sym; + uint32_t *location; + + DEBUGP("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_offset + + rel[i].r_offset; + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(rel[i].r_info); + + /* TODO: This is probably not correct */ + printk("Beware: untested code in module.c!\n"); + /* We add the value into the location given */ + *location += sym->st_value; + } + return 0; +} + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n", + me->name); + return -ENOEXEC; +} + +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + return 0; +} + +void module_arch_cleanup(struct module *mod) +{ +} diff --git a/arch/cris/kernel/process.c b/arch/cris/kernel/process.c index 60441877ef2f..23a5d13c18ea 100644 --- a/arch/cris/kernel/process.c +++ b/arch/cris/kernel/process.c @@ -1,13 +1,51 @@ -/* $Id: process.c,v 1.3 2002/01/21 15:22:49 bjornw Exp $ +/* $Id: process.c,v 1.14 2003/06/10 10:21:12 johana Exp $ * * linux/arch/cris/kernel/process.c * * Copyright (C) 1995 Linus Torvalds - * Copyright (C) 2000, 2001 Axis Communications AB + * Copyright (C) 2000-2002 Axis Communications AB * * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: process.c,v $ + * Revision 1.14 2003/06/10 10:21:12 johana + * Moved thread_saved_pc() from arch/cris/kernel/process.c to + * subarch specific process.c. + * + * Revision 1.13 2003/04/09 05:20:47 starvik + * Merge of Linux 2.5.67 + * + * Revision 1.12 2002/12/11 15:41:11 starvik + * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/kernel + * + * Revision 1.11 2002/12/10 09:00:10 starvik + * Merge of Linux 2.5.51 + * + * Revision 1.10 2002/11/27 08:42:34 starvik + * Argument to user_regs() is thread_info* + * + * Revision 1.9 2002/11/26 09:44:21 starvik + * New threads exits through ret_from_fork (necessary for preemptive scheduling) + * + * Revision 1.8 2002/11/19 14:35:24 starvik + * Changes from linux 2.4 + * Changed struct initializer syntax to the currently prefered notation + * + * Revision 1.7 2002/11/18 07:39:42 starvik + * thread_saved_pc moved here from processor.h + * + * Revision 1.6 2002/11/14 06:51:27 starvik + * Made cpu_idle more similar with other archs + * init_task_union -> init_thread_union + * Updated for new interrupt macros + * sys_clone and do_fork have a new argument, user_tid + * + * Revision 1.5 2002/11/05 06:45:11 starvik + * Merge of Linux 2.5.45 + * + * Revision 1.4 2002/02/05 15:37:44 bjornw + * Need init_task.h + * * Revision 1.3 2002/01/21 15:22:49 bjornw * current->counter is gone * @@ -54,29 +92,17 @@ */ #define __KERNEL_SYSCALLS__ -#include <stdarg.h> -#include <linux/errno.h> +#include <asm/atomic.h> +#include <asm/pgtable.h> +#include <asm/uaccess.h> +#include <linux/spinlock.h> +#include <linux/fs_struct.h> +#include <linux/init_task.h> #include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/stddef.h> -#include <linux/unistd.h> -#include <linux/ptrace.h> -#include <linux/slab.h> +#include <linux/fs.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/elfcore.h> -#include <linux/interrupt.h> -#include <linux/delay.h> - -#include <asm/uaccess.h> -#include <asm/pgtable.h> -#include <asm/system.h> -#include <asm/io.h> -#include <asm/processor.h> - -#include <linux/smp.h> //#define DEBUG @@ -89,20 +115,29 @@ static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; -static struct signal_struct init_signals = INIT_SIGNALS; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); struct mm_struct init_mm = INIT_MM(init_mm); /* - * Initial task structure. + * Initial thread structure. * * We need to make sure that this is 8192-byte aligned due to the * way process stacks are handled. This is done by having a special * "init_task" linker map entry.. */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task) }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); + -union task_union init_task_union - __attribute__((__section__(".data.init_task"))) = - { INIT_TASK(init_task_union.task) }; /* * The hlt_counter, disable_hlt and enable_hlt is just here as a hook if @@ -125,44 +160,34 @@ void enable_hlt(void) hlt_counter--; } -int cpu_idle(void *unused) -{ - while(1) { - schedule(); - } -} - -/* if the watchdog is enabled, we can simply disable interrupts and go - * into an eternal loop, and the watchdog will reset the CPU after 0.1s - * if on the other hand the watchdog wasn't enabled, we just enable it and wait +/* + * The following aren't currently used. */ +void (*pm_idle)(void); -void hard_reset_now (void) -{ - /* - * Don't declare this variable elsewhere. We don't want any other - * code to know about it than the watchdog handler in entry.S and - * this code, implementing hard reset through the watchdog. - */ -#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) - extern int cause_of_death; -#endif - - printk("*** HARD RESET ***\n"); - cli(); +extern void default_idle(void); -#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) - cause_of_death = 0xbedead; -#else - /* Since we don't plan to keep on reseting the watchdog, - the key can be arbitrary hence three */ - *R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, 3) | - IO_STATE(R_WATCHDOG, enable, start); -#endif - - while(1) /* waiting for RETRIBUTION! */ ; +/* + * 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 */ + while (1) { + void (*idle)(void) = pm_idle; + if (!idle) + idle = default_idle; + while (!need_resched()) + idle(); + schedule(); + } } +void hard_reset_now (void); + void machine_restart(void) { hard_reset_now(); @@ -194,60 +219,6 @@ void flush_thread(void) { } -asmlinkage void ret_from_sys_call(void); - -/* setup the child's kernel stack with a pt_regs and switch_stack on it. - * it will be un-nested during _resume and _ret_from_sys_call when the - * new thread is scheduled. - * - * also setup the thread switching structure which is used to keep - * thread-specific data during _resumes. - * - */ - -int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, - unsigned long unused, - struct task_struct *p, struct pt_regs *regs) -{ - struct pt_regs * childregs; - struct switch_stack *swstack; - - /* put the pt_regs structure at the end of the new kernel stack page and fix it up - * remember that the task_struct doubles as the kernel stack for the task - */ - - childregs = user_regs(p); - - *childregs = *regs; /* struct copy of pt_regs */ - - childregs->r10 = 0; /* child returns 0 after a fork/clone */ - - /* put the switch stack right below the pt_regs */ - - swstack = ((struct switch_stack *)childregs) - 1; - - swstack->r9 = 0; /* parameter to ret_from_sys_call, 0 == don't restart the syscall */ - - /* we want to return into ret_from_sys_call after the _resume */ - - swstack->return_ip = (unsigned long) ret_from_sys_call; - - /* fix the user-mode stackpointer */ - - p->thread.usp = usp; - - /* and the kernel-mode one */ - - p->thread.ksp = (unsigned long) swstack; - -#ifdef DEBUG - printk("copy_thread: new regs at 0x%p, as shown below:\n", childregs); - show_registers(childregs); -#endif - - return 0; -} - /* * fill in the user structure for a core dump.. */ @@ -281,110 +252,3 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) { return 0; } - -/* - * Be aware of the "magic" 7th argument in the four system-calls below. - * They need the latest stackframe, which is put as the 7th argument by - * entry.S. The previous arguments are dummies or actually used, but need - * to be defined to reach the 7th argument. - * - * N.B.: Another method to get the stackframe is to use current_regs(). But - * it returns the latest stack-frame stacked when going from _user mode_ and - * some of these (at least sys_clone) are called from kernel-mode sometimes - * (for example during kernel_thread, above) and thus cannot use it. Thus, - * to be sure not to get any surprises, we use the method for the other calls - * as well. - */ - -asmlinkage int sys_fork(long r10, long r11, long r12, long r13, long mof, long srp, - struct pt_regs *regs) -{ - struct task_struct *p; - p = do_fork(SIGCHLD, rdusp(), regs, 0); - return IS_ERR(p) ? PTR_ERR(p) : p->pid; -} - -/* if newusp is 0, we just grab the old usp */ - -asmlinkage int sys_clone(unsigned long newusp, unsigned long flags, - long r12, long r13, long mof, long srp, - struct pt_regs *regs) -{ - struct task_struct *p; - if (!newusp) - newusp = rdusp(); - p = do_fork(flags & ~CLONE_IDLETASK, newusp, regs, 0); - return IS_ERR(p) ? PTR_ERR(p) : p->pid; -} - -/* vfork is a system call in i386 because of register-pressure - maybe - * we can remove it and handle it in libc but we put it here until then. - */ - -asmlinkage int sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp, - struct pt_regs *regs) -{ - struct task_struct *p; - p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0); - return IS_ERR(p) ? PTR_ERR(p) : p->pid; -} - -/* - * sys_execve() executes a new program. - */ -asmlinkage int sys_execve(const char *fname, char **argv, char **envp, - long r13, long mof, long srp, - struct pt_regs *regs) -{ - int error; - char *filename; - - filename = getname(fname); - error = PTR_ERR(filename); - - if (IS_ERR(filename)) - goto out; - error = do_execve(filename, argv, envp, regs); - putname(filename); - out: - return error; -} - -/* - * These bracket the sleeping functions.. - */ - -extern void scheduling_functions_start_here(void); -extern void scheduling_functions_end_here(void); -#define first_sched ((unsigned long) scheduling_functions_start_here) -#define last_sched ((unsigned long) scheduling_functions_end_here) - -unsigned long get_wchan(struct task_struct *p) -{ -#if 0 - /* YURGH. TODO. */ - - unsigned long ebp, esp, eip; - unsigned long stack_page; - int count = 0; - if (!p || p == current || p->state == TASK_RUNNING) - return 0; - stack_page = (unsigned long)p; - esp = p->thread.esp; - if (!stack_page || esp < stack_page || esp > 8188+stack_page) - return 0; - /* include/asm-i386/system.h:switch_to() pushes ebp last. */ - ebp = *(unsigned long *) esp; - do { - if (ebp < stack_page || ebp > 8184+stack_page) - return 0; - eip = *(unsigned long *) (ebp+4); - if (eip < first_sched || eip >= last_sched) - return eip; - ebp = *(unsigned long *) ebp; - } while (count++ < 16); -#endif - return 0; -} -#undef last_sched -#undef first_sched diff --git a/arch/cris/kernel/ptrace.c b/arch/cris/kernel/ptrace.c index c8a066c4ee4c..e85a2fdd9acf 100644 --- a/arch/cris/kernel/ptrace.c +++ b/arch/cris/kernel/ptrace.c @@ -3,11 +3,35 @@ * * Parts taken from the m68k port. * - * Copyright (c) 2000, 2001 Axis Communications AB + * Copyright (c) 2000, 2001, 2002 Axis Communications AB * * Authors: Bjorn Wesen * * $Log: ptrace.c,v $ + * Revision 1.9 2003/07/04 12:56:11 tobiasa + * Moved arch-specific code to arch-specific files. + * + * Revision 1.8 2003/04/09 05:20:47 starvik + * Merge of Linux 2.5.67 + * + * Revision 1.7 2002/11/27 08:42:34 starvik + * Argument to user_regs() is thread_info* + * + * Revision 1.6 2002/11/20 11:56:11 starvik + * Merge of Linux 2.5.48 + * + * Revision 1.5 2002/11/18 07:41:19 starvik + * Removed warning + * + * Revision 1.4 2002/11/11 12:47:28 starvik + * SYSCALL_TRACE has been moved to thread flags + * + * Revision 1.3 2002/02/05 15:37:18 bjornw + * * Add do_notify_resume (replaces do_signal in the callchain) + * * syscall_trace is now do_syscall_trace + * * current->ptrace flag PT_TRACESYS -> PT_SYSCALLTRACE + * * Keep track of the current->work.syscall_trace counter + * * Revision 1.2 2001/12/18 13:35:20 bjornw * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). * @@ -49,18 +73,9 @@ #include <asm/processor.h> /* - * does not yet catch signals sent when the child dies. - * in exit.c or in signal.c. - */ - -/* determines which bits in DCCR the user has access to. */ -/* 1 = access 0 = no access */ -#define DCCR_MASK 0x0000001f /* XNZVC */ - -/* * Get contents of register REGNO in task TASK. */ -static inline long get_reg(struct task_struct *task, unsigned int regno) +inline long get_reg(struct task_struct *task, unsigned int regno) { /* USP is a special case, it's not in the pt_regs struct but * in the tasks thread struct @@ -69,7 +84,7 @@ static inline long get_reg(struct task_struct *task, unsigned int regno) if (regno == PT_USP) return task->thread.usp; else if (regno < PT_MAX) - return ((unsigned long *)user_regs(task))[regno]; + return ((unsigned long *)user_regs(task->thread_info))[regno]; else return 0; } @@ -77,248 +92,28 @@ static inline long get_reg(struct task_struct *task, unsigned int regno) /* * Write contents of register REGNO in task TASK. */ -static inline int put_reg(struct task_struct *task, unsigned int regno, +inline int put_reg(struct task_struct *task, unsigned int regno, unsigned long data) { if (regno == PT_USP) task->thread.usp = data; else if (regno < PT_MAX) - ((unsigned long *)user_regs(task))[regno] = data; + ((unsigned long *)user_regs(task->thread_info))[regno] = data; else return -1; return 0; } -/* - * Called by kernel/ptrace.c when detaching.. - * - * Make sure the single step bit is not set. +/* notification of userspace execution resumption + * - triggered by current->work.notify_resume */ -void ptrace_disable(struct task_struct *child) -{ - /* Todo - pending singlesteps? */ -} +extern int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs); -/* Note that this implementation of ptrace behaves differently from vanilla - * ptrace. Contrary to what the man page says, in the PTRACE_PEEKTEXT, - * PTRACE_PEEKDATA, and PTRACE_PEEKUSER requests the data variable is not - * ignored. Instead, the data variable is expected to point at a location - * (in user space) where the result of the ptrace call is written (instead of - * being returned). - */ -asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +void do_notify_resume(int canrestart, sigset_t *oldset, struct pt_regs *regs, + __u32 thread_info_flags ) { - struct task_struct *child; - int ret; - - lock_kernel(); - ret = -EPERM; - if (request == PTRACE_TRACEME) { - /* are we already being traced? */ - if (current->ptrace & PT_PTRACED) - goto out; - /* set the ptrace bit in the process flags. */ - current->ptrace |= PT_PTRACED; - ret = 0; - goto out; - } - ret = -ESRCH; - read_lock(&tasklist_lock); - child = find_task_by_pid(pid); - if (child) - get_task_struct(child); - read_unlock(&tasklist_lock); - if (!child) - goto out; - ret = -EPERM; - if (pid == 1) /* you may not mess with init */ - goto out_tsk; - if (request == PTRACE_ATTACH) { - ret = ptrace_attach(child); - goto out_tsk; - } - ret = -ESRCH; - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; - } - if (child->p_pptr != current) - goto out_tsk; - - switch (request) { - /* when I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp,(unsigned long *) data); - break; - } - - /* read the word at location addr in the USER area. */ - case PTRACE_PEEKUSR: { - unsigned long tmp; - - ret = -EIO; - if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) - break; - - tmp = 0; /* Default return condition */ - ret = -EIO; - if (addr < sizeof(struct pt_regs)) { - tmp = get_reg(child, addr >> 2); - ret = put_user(tmp, (unsigned long *)data); - } - break; - } - - /* when I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) - break; - ret = -EIO; - break; - - case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ - ret = -EIO; - if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) - break; - - if (addr < sizeof(struct pt_regs)) { - addr >>= 2; - - if (addr == PT_DCCR) { - /* don't allow the tracing process to change stuff like - * interrupt enable, kernel/user bit, dma enables etc. - */ - data &= DCCR_MASK; - data |= get_reg(child, PT_DCCR) & ~DCCR_MASK; - } - if (put_reg(child, addr, data)) - break; - ret = 0; - } - break; - - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: /* restart after signal. */ - ret = -EIO; - if ((unsigned long) data > _NSIG) - break; - if (request == PTRACE_SYSCALL) - child->ptrace |= PT_TRACESYS; - else - child->ptrace &= ~PT_TRACESYS; - child->exit_code = data; - /* TODO: make sure any pending breakpoint is killed */ - wake_up_process(child); - ret = 0; - break; - -/* - * make the child exit. Best I can do is send it a sigkill. - * perhaps it should be put in the status that it wants to - * exit. - */ - case PTRACE_KILL: - ret = 0; - if (child->state == TASK_ZOMBIE) /* already dead */ - break; - child->exit_code = SIGKILL; - /* TODO: make sure any pending breakpoint is killed */ - wake_up_process(child); - break; - - case PTRACE_SINGLESTEP: /* set the trap flag. */ - ret = -EIO; - if ((unsigned long) data > _NSIG) - break; - child->ptrace &= ~PT_TRACESYS; - - /* TODO: set some clever breakpoint mechanism... */ - - child->exit_code = data; - /* give it a chance to run. */ - wake_up_process(child); - ret = 0; - break; - - case PTRACE_DETACH: - ret = ptrace_detach(child, data); - break; - - case PTRACE_GETREGS: { /* Get all gp regs from the child. */ - int i; - unsigned long tmp; - for (i = 0; i <= PT_MAX; i++) { - tmp = get_reg(child, i); - if (put_user(tmp, (unsigned long *) data)) { - ret = -EFAULT; - break; - } - data += sizeof(long); - } - ret = 0; - break; - } - - case PTRACE_SETREGS: { /* Set all gp regs in the child. */ - int i; - unsigned long tmp; - for (i = 0; i <= PT_MAX; i++) { - if (get_user(tmp, (unsigned long *) data)) { - ret = -EFAULT; - break; - } - if (i == PT_DCCR) { - tmp &= DCCR_MASK; - tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK; - } - put_reg(child, i, tmp); - data += sizeof(long); - } - ret = 0; - break; - } - - default: - ret = ptrace_request(child, request, addr, data); - break; - } -out_tsk: - free_task_struct(child); -out: - unlock_kernel(); - return ret; -} - -asmlinkage void syscall_trace(void) -{ - if ((current->ptrace & (PT_PTRACED | PT_TRACESYS)) != - (PT_PTRACED | PT_TRACESYS)) - return; - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); - /* - * this isn't the same as continuing with a signal, but it will do - * for normal use. strace only continues with a signal if the - * stopping signal is not SIGTRAP. -brl - */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code = 0; - } + /* deal with pending signal delivery */ + if (thread_info_flags & _TIF_SIGPENDING) + do_signal(canrestart,oldset,regs); } diff --git a/arch/cris/kernel/setup.c b/arch/cris/kernel/setup.c index 3232548a639c..fc989d2c7735 100644 --- a/arch/cris/kernel/setup.c +++ b/arch/cris/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $ +/* $Id: setup.c,v 1.7 2003/07/04 08:27:52 starvik Exp $ * * linux/arch/cris/kernel/setup.c * @@ -10,30 +10,12 @@ * This file handles the architecture-dependent parts of initialization */ -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/stddef.h> -#include <linux/unistd.h> -#include <linux/ptrace.h> -#include <linux/slab.h> -#include <linux/user.h> -#include <linux/a.out.h> -#include <linux/tty.h> -#include <linux/ioport.h> -#include <linux/delay.h> -#include <linux/config.h> #include <linux/init.h> +#include <linux/mm.h> #include <linux/bootmem.h> -#include <linux/seq_file.h> - -#include <asm/segment.h> -#include <asm/system.h> -#include <asm/smp.h> #include <asm/pgtable.h> -#include <asm/types.h> -#include <asm/svinto.h> +#include <linux/seq_file.h> +#include <linux/tty.h> /* * Setup options @@ -52,6 +34,7 @@ static char command_line[COMMAND_LINE_SIZE] = { 0, }; char saved_command_line[COMMAND_LINE_SIZE]; extern const unsigned long text_start, edata; /* set by the linker script */ +extern unsigned long dram_start, dram_end; extern unsigned long romfs_start, romfs_length, romfs_in_flash; /* from head.S */ @@ -169,14 +152,14 @@ setup_arch(char **cmdline_p) *cmdline_p = command_line; - if (romfs_in_flash) { - strlcpy(command_line, "root=", sizeof(command_line)); - strlcat(command_line, CONFIG_ETRAX_ROOT_DEVICE, - sizeof(command_line)); - - /* Save command line copy for /proc/cmdline */ - strlcpy(saved_command_line, command_line, sizeof(saved_command_line)); - } +#ifdef CONFIG_ETRAX_CMDLINE + strlcpy(command_line, CONFIG_ETRAX_CMDLINE, sizeof(command_line)); +#elif defined(CONFIG_ETRAX_ROOT_DEVICE) + strlcpy(command_line, "root=", sizeof(command_line)); + strlcat(command_line, CONFIG_ETRAX_ROOT_DEVICE, + sizeof(command_line)); +#endif + command_line[COMMAND_LINE_SIZE - 1] = '\0'; /* give credit for the CRIS port */ @@ -184,83 +167,6 @@ setup_arch(char **cmdline_p) } -#ifdef CONFIG_PROC_FS -#define HAS_FPU 0x0001 -#define HAS_MMU 0x0002 -#define HAS_ETHERNET100 0x0004 -#define HAS_TOKENRING 0x0008 -#define HAS_SCSI 0x0010 -#define HAS_ATA 0x0020 -#define HAS_USB 0x0040 -#define HAS_IRQ_BUG 0x0080 -#define HAS_MMU_BUG 0x0100 - -static struct cpu_info { - char *model; - unsigned short cache; - unsigned short flags; -} cpu_info[] = { - /* The first four models will never ever run this code and are - only here for display. */ - { "ETRAX 1", 0, 0 }, - { "ETRAX 2", 0, 0 }, - { "ETRAX 3", 0, HAS_TOKENRING }, - { "ETRAX 4", 0, HAS_TOKENRING | HAS_SCSI }, - { "Unknown", 0, 0 }, - { "Unknown", 0, 0 }, - { "Unknown", 0, 0 }, - { "Simulator", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA }, - { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG }, - { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA }, - { "ETRAX 100LX", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU | HAS_MMU_BUG }, - { "ETRAX 100LX v2", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU }, - { "Unknown", 0, 0 } /* This entry MUST be the last */ -}; - -static int show_cpuinfo(struct seq_file *m, void *v) -{ - unsigned long revision; - struct cpu_info *info; - - /* read the version register in the CPU and print some stuff */ - - revision = rdvr(); - - if (revision >= sizeof cpu_info/sizeof *cpu_info) - info = &cpu_info[sizeof cpu_info/sizeof *cpu_info - 1]; - else - info = &cpu_info[revision]; - - return seq_printf(m, - "cpu\t\t: CRIS\n" - "cpu revision\t: %lu\n" - "cpu model\t: %s\n" - "cache size\t: %d kB\n" - "fpu\t\t: %s\n" - "mmu\t\t: %s\n" - "mmu DMA bug\t: %s\n" - "ethernet\t: %s Mbps\n" - "token ring\t: %s\n" - "scsi\t\t: %s\n" - "ata\t\t: %s\n" - "usb\t\t: %s\n" - "bogomips\t: %lu.%02lu\n", - - revision, - info->model, - info->cache, - info->flags & HAS_FPU ? "yes" : "no", - info->flags & HAS_MMU ? "yes" : "no", - info->flags & HAS_MMU_BUG ? "yes" : "no", - info->flags & HAS_ETHERNET100 ? "10/100" : "10", - info->flags & HAS_TOKENRING ? "4/16 Mbps" : "no", - info->flags & HAS_SCSI ? "yes" : "no", - info->flags & HAS_ATA ? "yes" : "no", - info->flags & HAS_USB ? "yes" : "no", - (loops_per_jiffy * HZ + 500) / 500000, - ((loops_per_jiffy * HZ + 500) / 5000) % 100); -} - static void *c_start(struct seq_file *m, loff_t *pos) { /* We only got one CPU... */ @@ -277,11 +183,13 @@ static void c_stop(struct seq_file *m, void *v) { } +extern int show_cpuinfo(struct seq_file *m, void *v); + struct seq_operations cpuinfo_op = { - .start = c_start, - .next = c_next, - .stop = c_stop, - .show = show_cpuinfo, + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, }; -#endif /* CONFIG_PROC_FS */ + diff --git a/arch/cris/kernel/sys_cris.c b/arch/cris/kernel/sys_cris.c index 31917ab11982..e7e5c04071e5 100644 --- a/arch/cris/kernel/sys_cris.c +++ b/arch/cris/kernel/sys_cris.c @@ -1,4 +1,4 @@ -/* $Id: sys_cris.c,v 1.1.1.1 2001/12/17 13:59:27 bjornw Exp $ +/* $Id: sys_cris.c,v 1.5 2003/07/04 08:27:52 starvik Exp $ * * linux/arch/cris/kernel/sys_cris.c * @@ -29,7 +29,7 @@ * sys_pipe() is the normal C calling standard for creating * a pipe. It's not the way Unix traditionally does this, though. */ -asmlinkage int sys_pipe(unsigned long * fildes) +asmlinkage int sys_pipe(unsigned long __user * fildes) { int fd[2]; int error; @@ -69,7 +69,7 @@ out: return error; } -asmlinkage unsigned long old_mmap(unsigned long *args) +asmlinkage unsigned long old_mmap(unsigned long __user *args) { unsigned long buffer[6]; int err = -EFAULT; @@ -101,7 +101,7 @@ sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot, */ asmlinkage int sys_ipc (uint call, int first, int second, - int third, void *ptr, long fifth) + int third, void __user *ptr, long fifth) { int version, ret; @@ -110,20 +110,24 @@ asmlinkage int sys_ipc (uint call, int first, int second, switch (call) { case SEMOP: - return sys_semop (first, (struct sembuf *)ptr, second); + return sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL); + case SEMTIMEDOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, second, + (const struct timespec __user *)fifth); + case SEMGET: return sys_semget (first, second, third); case SEMCTL: { union semun fourth; if (!ptr) return -EINVAL; - if (get_user(fourth.__pad, (void **) ptr)) + if (get_user(fourth.__pad, (void * __user *) ptr)) return -EFAULT; return sys_semctl (first, second, third, fourth); } case MSGSND: - return sys_msgsnd (first, (struct msgbuf *) ptr, + return sys_msgsnd (first, (struct msgbuf __user *) ptr, second, third); case MSGRCV: switch (version) { @@ -133,7 +137,7 @@ asmlinkage int sys_ipc (uint call, int first, int second, return -EINVAL; if (copy_from_user(&tmp, - (struct ipc_kludge *) ptr, + (struct ipc_kludge __user *) ptr, sizeof (tmp))) return -EFAULT; return sys_msgrcv (first, tmp.msgp, second, @@ -141,29 +145,29 @@ asmlinkage int sys_ipc (uint call, int first, int second, } default: return sys_msgrcv (first, - (struct msgbuf *) ptr, + (struct msgbuf __user *) ptr, second, fifth, third); } case MSGGET: return sys_msgget ((key_t) first, second); case MSGCTL: - return sys_msgctl (first, second, (struct msqid_ds *) ptr); + return sys_msgctl (first, second, (struct msqid_ds __user *) ptr); case SHMAT: { ulong raddr; - ret = sys_shmat (first, (char *) ptr, second, &raddr); + ret = sys_shmat (first, (char __user *) ptr, second, &raddr); if (ret) return ret; - return put_user (raddr, (ulong *) third); + return put_user (raddr, (ulong __user *) third); } case SHMDT: - return sys_shmdt ((char *)ptr); + return sys_shmdt ((char __user *)ptr); case SHMGET: return sys_shmget (first, second, third); case SHMCTL: return sys_shmctl (first, second, - (struct shmid_ds *) ptr); + (struct shmid_ds __user *) ptr); default: - return -EINVAL; + return -ENOSYS; } } diff --git a/arch/cris/kernel/time.c b/arch/cris/kernel/time.c index 81f91df573af..caee8e4fd39b 100644 --- a/arch/cris/kernel/time.c +++ b/arch/cris/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $ +/* $Id: time.c,v 1.9 2003/07/04 08:27:52 starvik Exp $ * * linux/arch/cris/kernel/time.c * @@ -22,124 +22,66 @@ * */ +#include <asm/rtc.h> #include <linux/errno.h> -#include <linux/sched.h> -#include <linux/init.h> -#include <linux/kernel.h> #include <linux/param.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/time.h> -#include <linux/delay.h> +#include <linux/jiffies.h> #include <linux/bcd.h> - -#include <asm/segment.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/delay.h> -#include <asm/rtc.h> - #include <linux/timex.h> -#include <linux/config.h> - -#include <asm/svinto.h> u64 jiffies_64 = INITIAL_JIFFIES; -static int have_rtc; /* used to remember if we have an RTC or not */ - -/* define this if you need to use print_timestamp */ -/* it will make jiffies at 96 hz instead of 100 hz though */ -#undef USE_CASCADE_TIMERS - -extern int setup_etrax_irq(int, struct irqaction *); - -/* Lookup table to convert *R_TIMER0 to microseconds (us) - * Timer goes from TIMER0_DIV down to 1 meaning 0-10000us in step of approx 52us - */ -unsigned short cris_timer0_value_us[TIMER0_DIV+1]; +int have_rtc; /* used to remember if we have an RTC or not */; #define TICK_SIZE tick -static unsigned long do_slow_gettimeoffset(void) -{ - unsigned long count; - unsigned long usec_count = 0; - - static unsigned long count_p = LATCH; /* for the first call after boot */ - static unsigned long jiffies_p = 0; - - /* - * cache volatile jiffies temporarily; we have IRQs turned off. - */ - unsigned long jiffies_t; - - /* The timer interrupt comes from Etrax timer 0. In order to get - * better precision, we check the current value. It might have - * underflowed already though. - */ - -#ifndef CONFIG_SVINTO_SIM - /* Not available in the xsim simulator. */ - count = *R_TIMER0_DATA; -#else - count = 0; -#endif - - jiffies_t = jiffies; - - /* - * avoiding timer inconsistencies (they are rare, but they happen)... - * there are three kinds of problems that must be avoided here: - * 1. the timer counter underflows - * 2. we are after the timer interrupt, but the bottom half handler - * hasn't executed yet. - */ - if( jiffies_t == jiffies_p ) { - if( count > count_p ) { - /* Timer wrapped */ - count = count_p; - usec_count = 1000000/CLOCK_TICK_RATE/2; - } - } else - jiffies_p = jiffies_t; - count_p = count; - /* Convert timer value to usec using table lookup */ - usec_count += cris_timer0_value_us[count]; -#if 0 - count = ((LATCH-1) - count) * TICK_SIZE; - count = (count + LATCH/2) / LATCH; -#endif - return usec_count; -} +extern unsigned long wall_jiffies; +extern unsigned long do_slow_gettimeoffset(void); static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; /* * This version of gettimeofday has near microsecond resolution. + * + * Note: Division is quite slow on CRIS and do_gettimeofday is called + * rather often. Maybe we should do some kind of approximation here + * (a naive approximation would be to divide by 1024). */ void do_gettimeofday(struct timeval *tv) { unsigned long flags; + signed long usec, sec; + local_irq_save(flags); + local_irq_disable(); + usec = do_gettimeoffset(); + { + unsigned long lost = jiffies - wall_jiffies; + if (lost) + usec += lost * (1000000 / HZ); + } + sec = xtime.tv_sec; + usec += xtime.tv_nsec / 1000; + local_irq_restore(flags); - save_flags(flags); - cli(); - *tv = xtime; - tv->tv_usec += do_gettimeoffset(); - if (tv->tv_usec >= 1000000) { - tv->tv_usec -= 1000000; - tv->tv_sec++; + while (usec >= 1000000) { + usec -= 1000000; + sec++; } - restore_flags(flags); + + tv->tv_sec = sec; + tv->tv_usec = usec; } int do_settimeofday(struct timespec *tv) { - if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) + unsigned long flags; + + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) return -EINVAL; - cli(); + local_irq_save(flags); + local_irq_disable(); + /* This is revolting. We need to set the xtime.tv_usec * correctly. However, the value in this location is * is value at the last tick. @@ -147,19 +89,20 @@ int do_settimeofday(struct timespec *tv) * would have done, and then undo it! */ tv->tv_nsec -= do_gettimeoffset() * 1000; + tv->tv_nsec -= (jiffies - wall_jiffies) * TICK_NSEC; - if (tv->tv_nsec < 0) { + while (tv->tv_nsec < 0) { tv->tv_nsec += NSEC_PER_SEC; tv->tv_sec--; } - - xtime = *tv; + xtime.tv_sec = tv->tv_sec; + xtime.tv_nsec = tv->tv_nsec; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - sti(); + local_irq_restore(flags); return 0; } @@ -169,7 +112,7 @@ int do_settimeofday(struct timespec *tv) * sets the minutes. Usually you'll only notice that after reboot! */ -static int set_rtc_mmss(unsigned long nowtime) +int set_rtc_mmss(unsigned long nowtime) { int retval = 0; int real_seconds, real_minutes, cmos_minutes; @@ -209,143 +152,6 @@ static int set_rtc_mmss(unsigned long nowtime) return retval; } -/* Excerpt from the Etrax100 HSDD about the built-in watchdog: - * - * 3.10.4 Watchdog timer - - * When the watchdog timer is started, it generates an NMI if the watchdog - * isn't restarted or stopped within 0.1 s. If it still isn't restarted or - * stopped after an additional 3.3 ms, the watchdog resets the chip. - * The watchdog timer is stopped after reset. The watchdog timer is controlled - * by the R_WATCHDOG register. The R_WATCHDOG register contains an enable bit - * and a 3-bit key value. The effect of writing to the R_WATCHDOG register is - * described in the table below: - * - * Watchdog Value written: - * state: To enable: To key: Operation: - * -------- ---------- ------- ---------- - * stopped 0 X No effect. - * stopped 1 key_val Start watchdog with key = key_val. - * started 0 ~key Stop watchdog - * started 1 ~key Restart watchdog with key = ~key. - * started X new_key_val Change key to new_key_val. - * - * Note: '~' is the bitwise NOT operator. - * - */ - -/* right now, starting the watchdog is the same as resetting it */ -#define start_watchdog reset_watchdog - -#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) -static int watchdog_key = 0; /* arbitrary number */ -#endif - -/* number of pages to consider "out of memory". it is normal that the memory - * is used though, so put this really low. - */ - -#define WATCHDOG_MIN_FREE_PAGES 8 - -void -reset_watchdog(void) -{ -#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) - /* only keep watchdog happy as long as we have memory left! */ - if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) { - /* reset the watchdog with the inverse of the old key */ - watchdog_key ^= 0x7; /* invert key, which is 3 bits */ - *R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, watchdog_key) | - IO_STATE(R_WATCHDOG, enable, start); - } -#endif -} - -/* stop the watchdog - we still need the correct key */ - -void -stop_watchdog(void) -{ -#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) - watchdog_key ^= 0x7; /* invert key, which is 3 bits */ - *R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, watchdog_key) | - IO_STATE(R_WATCHDOG, enable, stop); -#endif -} - -/* last time the cmos clock got updated */ -static long last_rtc_update = 0; - -/* - * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick - */ - -//static unsigned short myjiff; /* used by our debug routine print_timestamp */ - -static inline void -timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - /* acknowledge the timer irq */ - -#ifdef USE_CASCADE_TIMERS - *R_TIMER_CTRL = - IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) | - IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) | - IO_STATE( R_TIMER_CTRL, i1, clr) | - IO_STATE( R_TIMER_CTRL, tm1, run) | - IO_STATE( R_TIMER_CTRL, clksel1, cascade0) | - IO_STATE( R_TIMER_CTRL, i0, clr) | - IO_STATE( R_TIMER_CTRL, tm0, run) | - IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz); -#else - *R_TIMER_CTRL = r_timer_ctrl_shadow | - IO_STATE(R_TIMER_CTRL, i0, clr); -#endif - - /* reset watchdog otherwise it resets us! */ - - reset_watchdog(); - - /* call the real timer interrupt handler */ - - do_timer(regs); - - /* - * If we have an externally synchronized Linux clock, then update - * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be - * called as close as possible to 500 ms before the new second starts. - */ - - if ((time_status & STA_UNSYNC) == 0 && - xtime.tv_sec > last_rtc_update + 660 && - xtime.tv_usec > 500000 - (tick >> 1) && - xtime.tv_usec < 500000 + (tick >> 1)) { - if (set_rtc_mmss(xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; - else - last_rtc_update = xtime.tv_sec - 600; - } -} - -#if 0 -/* some old debug code for testing the microsecond timing of packets */ -static unsigned int lastjiff; - -void print_timestamp(const char *s) -{ - unsigned long flags; - unsigned int newjiff; - - save_flags(flags); - cli(); - newjiff = (myjiff << 16) | (unsigned short)(-*R_TIMER01_DATA); - printk("%s: %x (%x)\n", s, newjiff, newjiff - lastjiff); - lastjiff = newjiff; - restore_flags(flags); -} -#endif - /* grab the time from the RTC chip */ unsigned long @@ -385,116 +191,6 @@ update_xtime_from_cmos(void) { if(have_rtc) { xtime.tv_sec = get_cmos_time(); - xtime.tv_usec = 0; - } -} - -/* timer is SA_SHIRQ so drivers can add stuff to the timer irq chain - * it needs to be SA_INTERRUPT to make the jiffies update work properly - */ - -static struct irqaction irq2 = { timer_interrupt, SA_SHIRQ | SA_INTERRUPT, - 0, "timer", NULL, NULL}; - -void __init -time_init(void) -{ - int i; - /* probe for the RTC and read it if it exists */ - - if(RTC_INIT() < 0) { - /* no RTC, start at 1980 */ - xtime.tv_sec = 0; - xtime.tv_usec = 0; - have_rtc = 0; - } else { - /* get the current time */ - have_rtc = 1; - update_xtime_from_cmos(); - } - - /* Setup the etrax timers - * Base frequency is 19200 hz, divider 192 -> 100 hz as Linux wants - * In normal mode, we use timer0, so timer1 is free. In cascade - * mode (which we sometimes use for debugging) both timers are used. - * Remember that linux/timex.h contains #defines that rely on the - * timer settings below (hz and divide factor) !!! - */ - -#ifdef USE_CASCADE_TIMERS - *R_TIMER_CTRL = - IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) | - IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) | - IO_STATE( R_TIMER_CTRL, i1, nop) | - IO_STATE( R_TIMER_CTRL, tm1, stop_ld) | - IO_STATE( R_TIMER_CTRL, clksel1, cascade0) | - IO_STATE( R_TIMER_CTRL, i0, nop) | - IO_STATE( R_TIMER_CTRL, tm0, stop_ld) | - IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz); - - *R_TIMER_CTRL = r_timer_ctrl_shadow = - IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) | - IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) | - IO_STATE( R_TIMER_CTRL, i1, nop) | - IO_STATE( R_TIMER_CTRL, tm1, run) | - IO_STATE( R_TIMER_CTRL, clksel1, cascade0) | - IO_STATE( R_TIMER_CTRL, i0, nop) | - IO_STATE( R_TIMER_CTRL, tm0, run) | - IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz); -#else - *R_TIMER_CTRL = - IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) | - IO_FIELD(R_TIMER_CTRL, timerdiv0, 192) | - IO_STATE(R_TIMER_CTRL, i1, nop) | - IO_STATE(R_TIMER_CTRL, tm1, stop_ld) | - IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) | - IO_STATE(R_TIMER_CTRL, i0, nop) | - IO_STATE(R_TIMER_CTRL, tm0, stop_ld) | - IO_STATE(R_TIMER_CTRL, clksel0, c19k2Hz); - - *R_TIMER_CTRL = r_timer_ctrl_shadow = - IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) | - IO_FIELD(R_TIMER_CTRL, timerdiv0, 192) | - IO_STATE(R_TIMER_CTRL, i1, nop) | - IO_STATE(R_TIMER_CTRL, tm1, run) | - IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) | - IO_STATE(R_TIMER_CTRL, i0, nop) | - IO_STATE(R_TIMER_CTRL, tm0, run) | - IO_STATE(R_TIMER_CTRL, clksel0, c19k2Hz); -#endif - - for (i=0; i <= TIMER0_DIV; i++) { - /* We must be careful not to get overflow... */ - cris_timer0_value_us[TIMER0_DIV-i] = - (unsigned short)((unsigned long) - ((i*(1000000/HZ))/TIMER0_DIV)&0x0000FFFFL); + xtime.tv_nsec = 0; } - - *R_IRQ_MASK0_SET = - IO_STATE(R_IRQ_MASK0_SET, timer0, set); /* unmask the timer irq */ - - /* now actually register the timer irq handler that calls timer_interrupt() */ - - setup_etrax_irq(2, &irq2); /* irq 2 is the timer0 irq in etrax */ - - /* enable watchdog if we should use one */ - -#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) - printk("Enabling watchdog...\n"); - start_watchdog(); - - /* If we use the hardware watchdog, we want to trap it as an NMI - and dump registers before it resets us. For this to happen, we - must set the "m" NMI enable flag (which once set, is unset only - when an NMI is taken). - - The same goes for the external NMI, but that doesn't have any - driver or infrastructure support yet. */ - asm ("setf m"); - - *R_IRQ_MASK0_SET = - IO_STATE(R_IRQ_MASK0_SET, watchdog_nmi, set); - *R_VECT_MASK_SET = - IO_STATE(R_VECT_MASK_SET, nmi, set); -#endif } diff --git a/arch/cris/kernel/traps.c b/arch/cris/kernel/traps.c index 9666d32af626..5273241e072d 100644 --- a/arch/cris/kernel/traps.c +++ b/arch/cris/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.2 2001/12/18 13:35:20 bjornw Exp $ +/* $Id: traps.c,v 1.7 2003/07/04 08:27:52 starvik Exp $ * * linux/arch/cris/traps.c * @@ -6,7 +6,7 @@ * mechanism, as well as some general stack/register dumping * things. * - * Copyright (C) 2000,2001 Axis Communications AB + * Copyright (C) 2000-2002 Axis Communications AB * * Authors: Bjorn Wesen * Hans-Peter Nilsson @@ -14,19 +14,8 @@ */ #include <linux/init.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/ptrace.h> -#include <linux/timer.h> -#include <linux/mm.h> -#include <asm/uaccess.h> - -#include <asm/system.h> -#include <asm/segment.h> -#include <asm/io.h> #include <asm/pgtable.h> +#include <asm/uaccess.h> static int kstack_depth_to_print = 24; @@ -95,7 +84,7 @@ void show_trace_task(struct task_struct *tsk) */ void -show_stack(unsigned long *sp) +show_stack(struct task_struct *task, unsigned long *sp) { unsigned long *stack, addr; int i; @@ -105,8 +94,12 @@ show_stack(unsigned long *sp) * back trace. */ - if(sp == NULL) - sp = (unsigned long*)rdsp(); + if(sp == NULL) { + if (task) + sp = (unsigned long*)task->thread.ksp; + else + sp = (unsigned long*)rdsp(); + } stack = sp; @@ -144,123 +137,9 @@ show_stack() } #endif -void -show_registers(struct pt_regs * regs) -{ - /* We either use rdusp() - the USP register, which might not - correspond to the current process for all cases we're called, - or we use the current->thread.usp, which is not up to date for - the current process. Experience shows we want the USP - register. */ - unsigned long usp = rdusp(); - - printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n", - regs->irp, regs->srp, regs->dccr, usp, regs->mof ); - printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", - regs->r0, regs->r1, regs->r2, regs->r3); - printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", - regs->r4, regs->r5, regs->r6, regs->r7); - printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", - regs->r8, regs->r9, regs->r10, regs->r11); - printk("r12: %08lx r13: %08lx oR10: %08lx\n", - regs->r12, regs->r13, regs->orig_r10); - printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE); - printk("Process %s (pid: %d, stackpage=%08lx)\n", - current->comm, current->pid, (unsigned long)current); - - /* - * When in-kernel, we also print out the stack and code at the - * time of the fault.. - */ - if (! user_mode(regs)) { - int i; - - show_stack((unsigned long*)usp); - - /* Dump kernel stack if the previous dump wasn't one. */ - if (usp != 0) - show_stack (NULL); - - printk("\nCode: "); - if(regs->irp < PAGE_OFFSET) - goto bad; - - /* Often enough the value at regs->irp does not point to - the interesting instruction, which is most often the - _previous_ instruction. So we dump at an offset large - enough that instruction decoding should be in sync at - the interesting point, but small enough to fit on a row - (sort of). We point out the regs->irp location in a - ksymoops-friendly way by wrapping the byte for that - address in parentheses. */ - for(i = -12; i < 12; i++) - { - unsigned char c; - if(__get_user(c, &((unsigned char*)regs->irp)[i])) { -bad: - printk(" Bad IP value."); - break; - } - - if (i == 0) - printk("(%02x) ", c); - else - printk("%02x ", c); - } - printk("\n"); - } -} - -/* Called from entry.S when the watchdog has bitten - * We print out something resembling an oops dump, and if - * we have the nice doggy development flag set, we halt here - * instead of rebooting. - */ - -void -watchdog_bite_hook(struct pt_regs *regs) -{ -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - cli(); - stop_watchdog(); - show_registers(regs); - while(1) /* nothing */; -#else - show_registers(regs); -#endif -} - void dump_stack(void) { - show_stack(NULL); -} - -/* This is normally the 'Oops' routine */ -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; - -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - /* This printout might take too long and trigger the - * watchdog normally. If we're in the nice doggy - * development mode, stop the watchdog during printout. - */ - stop_watchdog(); -#endif - - printk("%s: %04lx\n", str, err & 0xffff); - - show_registers(regs); - -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - reset_watchdog(); -#endif - do_exit(SIGSEGV); + show_stack(NULL, NULL); } void __init diff --git a/arch/cris/mm/Makefile b/arch/cris/mm/Makefile index 5a92ec53ca3d..f3790c7e41ae 100644 --- a/arch/cris/mm/Makefile +++ b/arch/cris/mm/Makefile @@ -3,3 +3,4 @@ # obj-y := init.o fault.o tlb.o extable.o ioremap.o + diff --git a/arch/cris/mm/extable.c b/arch/cris/mm/extable.c index 7351f59f8bb9..7992bdb4fea1 100644 --- a/arch/cris/mm/extable.c +++ b/arch/cris/mm/extable.c @@ -2,8 +2,18 @@ * linux/arch/cris/mm/extable.c * * $Log: extable.c,v $ - * Revision 1.1.1.1 2001/12/17 13:59:27 bjornw - * Import of Linux 2.5.1 + * Revision 1.4 2003/01/09 14:42:52 starvik + * Merge of Linux 2.5.55 + * + * Revision 1.3 2002/11/21 07:24:54 starvik + * Made search_exception_table similar to implementation for other archs + * (now compiles with CONFIG_MODULES) + * + * Revision 1.2 2002/11/18 07:36:55 starvik + * Removed warning + * + * Revision 1.1 2001/12/17 13:59:27 bjornw + * Initial revision * * Revision 1.3 2001/09/27 13:52:40 bjornw * Harmonize underscore-ness with other parts @@ -15,13 +25,11 @@ #include <linux/module.h> #include <asm/uaccess.h> -extern const struct exception_table_entry __start___ex_table[]; -extern const struct exception_table_entry __stop___ex_table[]; - -static inline unsigned long -search_one_table(const struct exception_table_entry *first, - const struct exception_table_entry *last, - unsigned long value) +/* Simple binary search */ +const struct exception_table_entry * +search_extable(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) { while (first <= last) { const struct exception_table_entry *mid; @@ -30,34 +38,11 @@ search_one_table(const struct exception_table_entry *first, mid = (last - first) / 2 + first; diff = mid->insn - value; if (diff == 0) - return mid->fixup; + return mid; else if (diff < 0) first = mid+1; else last = mid-1; } - return 0; -} - -unsigned long -search_exception_table(unsigned long addr) -{ - unsigned long ret; - -#ifndef CONFIG_MODULES - /* There is only the kernel to search. */ - return search_one_table(__start___ex_table, __stop___ex_table-1, addr); -#else - /* The kernel is the last "module" -- no need to treat it special. */ - struct module *mp; - for (mp = module_list; mp != NULL; mp = mp->next) { - if (mp->ex_table_start == NULL) - continue; - ret = search_one_table(mp->ex_table_start, - mp->ex_table_end - 1, addr); - if (ret) return ret; - } -#endif - - return 0; + return NULL; } diff --git a/arch/cris/mm/fault.c b/arch/cris/mm/fault.c index 2e4d91e40cf9..1af6f3a49dad 100644 --- a/arch/cris/mm/fault.c +++ b/arch/cris/mm/fault.c @@ -6,6 +6,25 @@ * Authors: Bjorn Wesen * * $Log: fault.c,v $ + * Revision 1.8 2003/07/04 13:02:48 tobiasa + * Moved code snippet from arch/cris/mm/fault.c that searches for fixup code + * to seperate function in arch-specific files. + * + * Revision 1.7 2003/01/22 06:48:38 starvik + * Fixed warnings issued by GCC 3.2.1 + * + * Revision 1.6 2003/01/09 14:42:52 starvik + * Merge of Linux 2.5.55 + * + * Revision 1.5 2002/12/11 14:44:48 starvik + * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/mm + * + * Revision 1.4 2002/11/13 15:10:28 starvik + * pte_offset has been renamed to pte_offset_kernel + * + * Revision 1.3 2002/11/05 06:45:13 starvik + * Merge of Linux 2.5.45 + * * Revision 1.2 2001/12/18 13:35:22 bjornw * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). * @@ -68,24 +87,13 @@ * */ -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/types.h> -#include <linux/ptrace.h> -#include <linux/mman.h> #include <linux/mm.h> #include <linux/interrupt.h> - -#include <asm/system.h> -#include <asm/segment.h> -#include <asm/pgtable.h> +#include <linux/module.h> #include <asm/uaccess.h> -#include <asm/svinto.h> -extern void die_if_kernel(const char *,struct pt_regs *,long); +extern int find_fixup_code(struct pt_regs *); +extern void die_if_kernel(const char *, struct pt_regs *, long); asmlinkage void do_invalid_op (struct pt_regs *, unsigned long); asmlinkage void do_page_fault(unsigned long address, struct pt_regs *regs, @@ -107,127 +115,6 @@ asmlinkage void do_page_fault(unsigned long address, struct pt_regs *regs, volatile pgd_t *current_pgd; -/* fast TLB-fill fault handler - * this is called from entry.S with interrupts disabled - */ - -void -handle_mmu_bus_fault(struct pt_regs *regs) -{ - int cause, select; -#ifdef DEBUG - int index; - int page_id; - int acc, inv; -#endif - int miss, we, writeac; - pmd_t *pmd; - pte_t pte; - int errcode; - unsigned long address; - - cause = *R_MMU_CAUSE; - select = *R_TLB_SELECT; - - address = cause & PAGE_MASK; /* get faulting address */ - -#ifdef DEBUG - page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause); - acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause); - inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause); - index = IO_EXTRACT(R_TLB_SELECT, index, select); -#endif - miss = IO_EXTRACT(R_MMU_CAUSE, miss_excp, cause); - we = IO_EXTRACT(R_MMU_CAUSE, we_excp, cause); - writeac = IO_EXTRACT(R_MMU_CAUSE, wr_rd, cause); - - /* ETRAX 100LX TR89 bugfix: if the second half of an unaligned - * write causes a MMU-fault, it will not be restarted correctly. - * This could happen if a write crosses a page-boundary and the - * second page is not yet COW'ed or even loaded. The workaround - * is to clear the unaligned bit in the CPU status record, so - * that the CPU will rerun both the first and second halves of - * the instruction. This will not have any sideeffects unless - * the first half goes to any device or memory that can't be - * written twice, and which is mapped through the MMU. - * - * We only need to do this for writes. - */ - - if(writeac) - regs->csrinstr &= ~(1 << 5); - - /* Set errcode's R/W flag according to the mode which caused the - * fault - */ - - errcode = writeac << 1; - - D(printk("bus_fault from IRP 0x%lx: addr 0x%lx, miss %d, inv %d, we %d, acc %d, dx %d pid %d\n", - regs->irp, address, miss, inv, we, acc, index, page_id)); - - /* for a miss, we need to reload the TLB entry */ - - if (miss) { - /* see if the pte exists at all - * refer through current_pgd, don't use mm->pgd - */ - - pmd = (pmd_t *)(current_pgd + pgd_index(address)); - if (pmd_none(*pmd)) - goto dofault; - if (pmd_bad(*pmd)) { - printk("bad pgdir entry 0x%lx at 0x%p\n", *(unsigned long*)pmd, pmd); - pmd_clear(pmd); - return; - } - pte = *pte_offset(pmd, address); - if (!pte_present(pte)) - goto dofault; - -#ifdef DEBUG - printk(" found pte %lx pg %p ", pte_val(pte), pte_page(pte)); - if (pte_val(pte) & _PAGE_SILENT_WRITE) - printk("Silent-W "); - if (pte_val(pte) & _PAGE_KERNEL) - printk("Kernel "); - if (pte_val(pte) & _PAGE_SILENT_READ) - printk("Silent-R "); - if (pte_val(pte) & _PAGE_GLOBAL) - printk("Global "); - if (pte_val(pte) & _PAGE_PRESENT) - printk("Present "); - if (pte_val(pte) & _PAGE_ACCESSED) - printk("Accessed "); - if (pte_val(pte) & _PAGE_MODIFIED) - printk("Modified "); - if (pte_val(pte) & _PAGE_READ) - printk("Readable "); - if (pte_val(pte) & _PAGE_WRITE) - printk("Writeable "); - printk("\n"); -#endif - - /* load up the chosen TLB entry - * this assumes the pte format is the same as the TLB_LO layout. - * - * the write to R_TLB_LO also writes the vpn and page_id fields from - * R_MMU_CAUSE, which we in this case obviously want to keep - */ - - *R_TLB_LO = pte_val(pte); - - return; - } - - errcode = 1 | (we << 1); - - dofault: - /* leave it to the MM system fault handler below */ - D(printk("do_page_fault %lx errcode %d\n", address, errcode)); - do_page_fault(address, regs, errcode); -} - /* * This routine handles page faults. It determines the address, * and the problem, and then passes it off to one of the appropriate @@ -253,7 +140,6 @@ do_page_fault(unsigned long address, struct pt_regs *regs, struct mm_struct *mm; struct vm_area_struct * vma; int writeaccess; - unsigned long fixup; siginfo_t info; tsk = current; @@ -391,20 +277,8 @@ do_page_fault(unsigned long address, struct pt_regs *regs, * code) */ - if ((fixup = search_exception_table(regs->irp)) != 0) { - /* Adjust the instruction pointer in the stackframe */ - - regs->irp = fixup; - - /* We do not want to return by restoring the CPU-state - * anymore, so switch frame-types (see ptrace.h) - */ - - regs->frametype = CRIS_FRAME_NORMAL; - - D(printk("doing fixup to 0x%lx\n", fixup)); + if (find_fixup_code(regs)) return; - } /* * Oops. The kernel tried to access some bad page. We'll have to @@ -498,7 +372,7 @@ vmalloc_fault: * silently loop forever. */ - pte_k = pte_offset(pmd_k, address); + pte_k = pte_offset_kernel(pmd_k, address); if (!pte_present(*pte_k)) goto no_context; diff --git a/arch/cris/mm/init.c b/arch/cris/mm/init.c index d9ab23bacb2b..3ff9bfcb8194 100644 --- a/arch/cris/mm/init.c +++ b/arch/cris/mm/init.c @@ -7,6 +7,27 @@ * Authors: Bjorn Wesen (bjornw@axis.com) * * $Log: init.c,v $ + * Revision 1.9 2003/07/04 08:27:54 starvik + * Merge of Linux 2.5.74 + * + * Revision 1.8 2003/04/09 05:20:48 starvik + * Merge of Linux 2.5.67 + * + * Revision 1.7 2003/01/22 06:48:38 starvik + * Fixed warnings issued by GCC 3.2.1 + * + * Revision 1.6 2002/12/11 14:44:48 starvik + * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/mm + * + * Revision 1.5 2002/11/18 07:37:37 starvik + * Added cache bug workaround (from Linux 2.4) + * + * Revision 1.4 2002/11/13 15:40:24 starvik + * Removed the page table cache stuff (as done in other archs) + * + * Revision 1.3 2002/11/05 06:45:13 starvik + * Merge of Linux 2.5.45 + * * Revision 1.2 2001/12/18 13:35:22 bjornw * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). * @@ -91,65 +112,19 @@ * */ -#include <linux/config.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/types.h> -#include <linux/ptrace.h> -#include <linux/mman.h> -#include <linux/mm.h> -#include <linux/swap.h> -#include <linux/smp.h> +#include <linux/init.h> #include <linux/bootmem.h> +#include <asm/tlb.h> -#include <asm/system.h> -#include <asm/segment.h> -#include <asm/pgalloc.h> -#include <asm/pgtable.h> -#include <asm/dma.h> -#include <asm/svinto.h> -#include <asm/io.h> -#include <asm/mmu_context.h> - -struct pgtable_cache_struct quicklists; /* see asm/pgalloc.h */ - -const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n"; - -extern void die_if_kernel(char *,struct pt_regs *,long); -extern void show_net_buffers(void); -extern void tlb_init(void); - +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); unsigned long empty_zero_page; -/* trim the page-table cache if necessary */ - -int -do_check_pgt_cache(int low, int high) -{ - int freed = 0; +extern unsigned long loops_per_jiffy; /* init/main.c */ +unsigned long loops_per_usec; - if(pgtable_cache_size > high) { - do { - if(pgd_quicklist) { - free_pgd_slow(get_pgd_fast()); - freed++; - } - if(pmd_quicklist) { - pmd_free_slow(pmd_alloc_one_fast(NULL, 0)); - freed++; - } - if(pte_quicklist) { - pte_free_slow(pte_alloc_one_fast(NULL, 0)); - freed++; - } - } while(pgtable_cache_size > low); - } - return freed; -} +extern char _stext, _edata, _etext; /* From linkerscript */ +extern char __init_begin, __init_end; void show_mem(void) @@ -180,181 +155,8 @@ show_mem(void) printk("%d pages nonshared\n",nonshared); printk("%d pages shared\n",shared); printk("%d pages swap cached\n",cached); - printk("%ld pages in page table cache\n",pgtable_cache_size); } -/* - * The kernel is already mapped with a kernel segment at kseg_c so - * we don't need to map it with a page table. However head.S also - * temporarily mapped it at kseg_4 so we should set up the ksegs again, - * clear the TLB and do some other paging setup stuff. - */ - -void __init -paging_init(void) -{ - int i; - unsigned long zones_size[MAX_NR_ZONES]; - - printk("Setting up paging and the MMU.\n"); - - /* clear out the init_mm.pgd that will contain the kernel's mappings */ - - for(i = 0; i < PTRS_PER_PGD; i++) - swapper_pg_dir[i] = __pgd(0); - - /* make sure the current pgd table points to something sane - * (even if it is most probably not used until the next - * switch_mm) - */ - - current_pgd = init_mm.pgd; - - /* initialise the TLB (tlb.c) */ - - tlb_init(); - - /* see README.mm for details on the KSEG setup */ - -#ifndef CONFIG_CRIS_LOW_MAP - /* This code is for the corrected Etrax-100 LX version 2... */ - - *R_MMU_KSEG = ( IO_STATE(R_MMU_KSEG, seg_f, seg ) | /* cached flash */ - IO_STATE(R_MMU_KSEG, seg_e, seg ) | /* uncached flash */ - IO_STATE(R_MMU_KSEG, seg_d, page ) | /* vmalloc area */ - IO_STATE(R_MMU_KSEG, seg_c, seg ) | /* kernel area */ - IO_STATE(R_MMU_KSEG, seg_b, seg ) | /* kernel reg area */ - IO_STATE(R_MMU_KSEG, seg_a, page ) | /* user area */ - IO_STATE(R_MMU_KSEG, seg_9, page ) | - IO_STATE(R_MMU_KSEG, seg_8, page ) | - IO_STATE(R_MMU_KSEG, seg_7, page ) | - IO_STATE(R_MMU_KSEG, seg_6, page ) | - IO_STATE(R_MMU_KSEG, seg_5, page ) | - IO_STATE(R_MMU_KSEG, seg_4, page ) | - IO_STATE(R_MMU_KSEG, seg_3, page ) | - IO_STATE(R_MMU_KSEG, seg_2, page ) | - IO_STATE(R_MMU_KSEG, seg_1, page ) | - IO_STATE(R_MMU_KSEG, seg_0, page ) ); - - *R_MMU_KBASE_HI = ( IO_FIELD(R_MMU_KBASE_HI, base_f, 0x0 ) | - IO_FIELD(R_MMU_KBASE_HI, base_e, 0x8 ) | - IO_FIELD(R_MMU_KBASE_HI, base_d, 0x0 ) | - IO_FIELD(R_MMU_KBASE_HI, base_c, 0x4 ) | - IO_FIELD(R_MMU_KBASE_HI, base_b, 0xb ) | - IO_FIELD(R_MMU_KBASE_HI, base_a, 0x0 ) | - IO_FIELD(R_MMU_KBASE_HI, base_9, 0x0 ) | - IO_FIELD(R_MMU_KBASE_HI, base_8, 0x0 ) ); - - *R_MMU_KBASE_LO = ( IO_FIELD(R_MMU_KBASE_LO, base_7, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_6, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_5, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_4, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_3, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_2, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_1, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_0, 0x0 ) ); -#else - /* Etrax-100 LX version 1 has a bug so that we cannot map anything - * across the 0x80000000 boundary, so we need to shrink the user-virtual - * area to 0x50000000 instead of 0xb0000000 and map things slightly - * different. The unused areas are marked as paged so that we can catch - * freak kernel accesses there. - * - * The ARTPEC chip is mapped at 0xa so we pass that segment straight - * through. We cannot vremap it because the vmalloc area is below 0x8 - * and Juliette needs an uncached area above 0x8. - * - * Same thing with 0xc and 0x9, which is memory-mapped I/O on some boards. - * We map them straight over in LOW_MAP, but use vremap in LX version 2. - */ - - *R_MMU_KSEG = ( IO_STATE(R_MMU_KSEG, seg_f, page ) | - IO_STATE(R_MMU_KSEG, seg_e, page ) | - IO_STATE(R_MMU_KSEG, seg_d, page ) | - IO_STATE(R_MMU_KSEG, seg_c, page ) | - IO_STATE(R_MMU_KSEG, seg_b, seg ) | /* kernel reg area */ -#ifdef CONFIG_JULIETTE - IO_STATE(R_MMU_KSEG, seg_a, seg ) | /* ARTPEC etc. */ -#else - IO_STATE(R_MMU_KSEG, seg_a, page ) | -#endif - IO_STATE(R_MMU_KSEG, seg_9, seg ) | /* LED's on some boards */ - IO_STATE(R_MMU_KSEG, seg_8, seg ) | /* CSE0/1, flash and I/O */ - IO_STATE(R_MMU_KSEG, seg_7, page ) | /* kernel vmalloc area */ - IO_STATE(R_MMU_KSEG, seg_6, seg ) | /* kernel DRAM area */ - IO_STATE(R_MMU_KSEG, seg_5, seg ) | /* cached flash */ - IO_STATE(R_MMU_KSEG, seg_4, page ) | /* user area */ - IO_STATE(R_MMU_KSEG, seg_3, page ) | /* user area */ - IO_STATE(R_MMU_KSEG, seg_2, page ) | /* user area */ - IO_STATE(R_MMU_KSEG, seg_1, page ) | /* user area */ - IO_STATE(R_MMU_KSEG, seg_0, page ) ); /* user area */ - - *R_MMU_KBASE_HI = ( IO_FIELD(R_MMU_KBASE_HI, base_f, 0x0 ) | - IO_FIELD(R_MMU_KBASE_HI, base_e, 0x0 ) | - IO_FIELD(R_MMU_KBASE_HI, base_d, 0x0 ) | - IO_FIELD(R_MMU_KBASE_HI, base_c, 0x0 ) | - IO_FIELD(R_MMU_KBASE_HI, base_b, 0xb ) | -#ifdef CONFIG_JULIETTE - IO_FIELD(R_MMU_KBASE_HI, base_a, 0xa ) | -#else - IO_FIELD(R_MMU_KBASE_HI, base_a, 0x0 ) | -#endif - IO_FIELD(R_MMU_KBASE_HI, base_9, 0x9 ) | - IO_FIELD(R_MMU_KBASE_HI, base_8, 0x8 ) ); - - *R_MMU_KBASE_LO = ( IO_FIELD(R_MMU_KBASE_LO, base_7, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_6, 0x4 ) | - IO_FIELD(R_MMU_KBASE_LO, base_5, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_4, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_3, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_2, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_1, 0x0 ) | - IO_FIELD(R_MMU_KBASE_LO, base_0, 0x0 ) ); -#endif - - *R_MMU_CONTEXT = ( IO_FIELD(R_MMU_CONTEXT, page_id, 0 ) ); - - /* The MMU has been enabled ever since head.S but just to make - * it totally obvious we do it here as well. - */ - - *R_MMU_CTRL = ( IO_STATE(R_MMU_CTRL, inv_excp, enable ) | - IO_STATE(R_MMU_CTRL, acc_excp, enable ) | - IO_STATE(R_MMU_CTRL, we_excp, enable ) ); - - *R_MMU_ENABLE = IO_STATE(R_MMU_ENABLE, mmu_enable, enable); - - /* - * initialize the bad page table and bad page to point - * to a couple of allocated pages - */ - - empty_zero_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE); - memset((void *)empty_zero_page, 0, PAGE_SIZE); - - /* All pages are DMA'able in Etrax, so put all in the DMA'able zone */ - - zones_size[0] = ((unsigned long)high_memory - PAGE_OFFSET) >> PAGE_SHIFT; - - for (i = 1; i < MAX_NR_ZONES; i++) - zones_size[i] = 0; - - /* Use free_area_init_node instead of free_area_init, because the former - * is designed for systems where the DRAM starts at an address substantially - * higher than 0, like us (we start at PAGE_OFFSET). This saves space in the - * mem_map page array. - */ - - free_area_init_node(0, &contig_page_data, 0, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); - mem_map = contig_page_data.node_mem_map; -} - -extern unsigned long loops_per_jiffy; /* init/main.c */ -unsigned long loops_per_usec; - -extern char _stext, _edata, _etext; -extern char __init_begin, __init_end; - void __init mem_init(void) { @@ -409,48 +211,6 @@ mem_init(void) 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. - */ - -void -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 - } -} - - /* free the pages occupied by initialization code */ void @@ -466,5 +226,5 @@ free_initmem(void) totalram_pages++; } printk ("Freeing unused kernel memory: %luk freed\n", - (&__init_end - &__init_begin) >> 10); + (unsigned long)((&__init_end - &__init_begin) >> 10)); } diff --git a/arch/cris/mm/ioremap.c b/arch/cris/mm/ioremap.c index 53656f9c5dc4..14216d9668f9 100644 --- a/arch/cris/mm/ioremap.c +++ b/arch/cris/mm/ioremap.c @@ -12,12 +12,13 @@ #include <linux/vmalloc.h> #include <asm/io.h> #include <asm/pgalloc.h> +#include <asm/cacheflush.h> +#include <asm/tlbflush.h> -static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, +extern inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, unsigned long phys_addr, unsigned long flags) { unsigned long end; - unsigned long pfn; address &= ~PMD_MASK; end = address + size; @@ -25,17 +26,16 @@ static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned l end = PMD_SIZE; if (address >= end) BUG(); - pfn = phys_addr >> PAGE_SHIFT; do { if (!pte_none(*pte)) { printk("remap_area_pte: page already exists\n"); BUG(); } - set_pte(pte, pfn_pte(pfn, __pgprot(_PAGE_PRESENT | __READABLE | - __WRITEABLE | _PAGE_GLOBAL | - _PAGE_KERNEL | flags))); + set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | __READABLE | + __WRITEABLE | _PAGE_GLOBAL | + _PAGE_KERNEL | flags))); address += PAGE_SIZE; - pfn++; + phys_addr += PAGE_SIZE; pte++; } while (address && (address < end)); } @@ -53,7 +53,7 @@ static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo if (address >= end) BUG(); do { - pte_t * pte = pte_alloc(&init_mm, pmd, address); + pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); if (!pte) return -ENOMEM; remap_area_pte(pte, address, end - address, address + phys_addr, flags); @@ -148,7 +148,7 @@ void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flag */ 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 --git a/arch/cris/mm/tlb.c b/arch/cris/mm/tlb.c index f5a97c9799fd..23eca5ad7389 100644 --- a/arch/cris/mm/tlb.c +++ b/arch/cris/mm/tlb.c @@ -7,31 +7,11 @@ * */ -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/types.h> -#include <linux/ptrace.h> -#include <linux/mman.h> -#include <linux/mm.h> #include <linux/init.h> - -#include <asm/system.h> -#include <asm/segment.h> -#include <asm/pgtable.h> -#include <asm/svinto.h> -#include <asm/mmu_context.h> +#include <asm/tlb.h> #define D(x) -/* CRIS in Etrax100LX TLB */ - -#define NUM_TLB_ENTRIES 64 -#define NUM_PAGEID 64 -#define INVALID_PAGEID 63 -#define NO_CONTEXT -1 - /* The TLB can host up to 64 different mm contexts at the same time. * The running context is R_MMU_CONTEXT, and each TLB entry contains a * page_id that has to match to give a hit. In page_id_map, we keep track @@ -47,182 +27,8 @@ */ struct mm_struct *page_id_map[NUM_PAGEID]; - static int map_replace_ptr = 1; /* which page_id_map entry to replace next */ -/* invalidate all TLB entries */ - -void -flush_tlb_all(void) -{ - int i; - unsigned long flags; - - /* the vpn of i & 0xf is so we don't write similar TLB entries - * in the same 4-way entry group. details.. - */ - - save_and_cli(flags); /* flush needs to be atomic */ - for(i = 0; i < NUM_TLB_ENTRIES; i++) { - *R_TLB_SELECT = ( IO_FIELD(R_TLB_SELECT, index, i) ); - *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | - IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); - - *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | - IO_STATE(R_TLB_LO, valid, no ) | - IO_STATE(R_TLB_LO, kernel,no ) | - IO_STATE(R_TLB_LO, we, no ) | - IO_FIELD(R_TLB_LO, pfn, 0 ) ); - } - restore_flags(flags); - D(printk("tlb: flushed all\n")); -} - -/* invalidate the selected mm context only */ - -void -flush_tlb_mm(struct mm_struct *mm) -{ - int i; - int page_id = mm->context; - unsigned long flags; - - D(printk("tlb: flush mm context %d (%p)\n", page_id, mm)); - - if(page_id == NO_CONTEXT) - return; - - /* mark the TLB entries that match the page_id as invalid. - * here we could also check the _PAGE_GLOBAL bit and NOT flush - * global pages. is it worth the extra I/O ? - */ - - save_and_cli(flags); /* flush needs to be atomic */ - for(i = 0; i < NUM_TLB_ENTRIES; i++) { - *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i); - if (IO_EXTRACT(R_TLB_HI, page_id, *R_TLB_HI) == page_id) { - *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | - IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); - - *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | - IO_STATE(R_TLB_LO, valid, no ) | - IO_STATE(R_TLB_LO, kernel,no ) | - IO_STATE(R_TLB_LO, we, no ) | - IO_FIELD(R_TLB_LO, pfn, 0 ) ); - } - } - restore_flags(flags); -} - -/* invalidate a single page */ - -void -flush_tlb_page(struct vm_area_struct *vma, - unsigned long addr) -{ - struct mm_struct *mm = vma->vm_mm; - int page_id = mm->context; - int i; - unsigned long flags; - - D(printk("tlb: flush page %p in context %d (%p)\n", addr, page_id, mm)); - - if(page_id == NO_CONTEXT) - return; - - addr &= PAGE_MASK; /* perhaps not necessary */ - - /* invalidate those TLB entries that match both the mm context - * and the virtual address requested - */ - - save_and_cli(flags); /* flush needs to be atomic */ - for(i = 0; i < NUM_TLB_ENTRIES; i++) { - unsigned long tlb_hi; - *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i); - tlb_hi = *R_TLB_HI; - if (IO_EXTRACT(R_TLB_HI, page_id, tlb_hi) == page_id && - (tlb_hi & PAGE_MASK) == addr) { - *R_TLB_HI = IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | - addr; /* same addr as before works. */ - - *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | - IO_STATE(R_TLB_LO, valid, no ) | - IO_STATE(R_TLB_LO, kernel,no ) | - IO_STATE(R_TLB_LO, we, no ) | - IO_FIELD(R_TLB_LO, pfn, 0 ) ); - } - } - restore_flags(flags); -} - -/* invalidate a page range */ - -void -flush_tlb_range(struct vm_area_struct *vma, - unsigned long start, - unsigned long end) -{ - struct mm_struct *mm = vma->vm_mm; - int page_id = mm->context; - int i; - unsigned long flags; - - D(printk("tlb: flush range %p<->%p in context %d (%p)\n", - start, end, page_id, mm)); - - if(page_id == NO_CONTEXT) - return; - - start &= PAGE_MASK; /* probably not necessary */ - end &= PAGE_MASK; /* dito */ - - /* invalidate those TLB entries that match both the mm context - * and the virtual address range - */ - - save_and_cli(flags); /* flush needs to be atomic */ - for(i = 0; i < NUM_TLB_ENTRIES; i++) { - unsigned long tlb_hi, vpn; - *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i); - tlb_hi = *R_TLB_HI; - vpn = tlb_hi & PAGE_MASK; - if (IO_EXTRACT(R_TLB_HI, page_id, tlb_hi) == page_id && - vpn >= start && vpn < end) { - *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | - IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); - - *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | - IO_STATE(R_TLB_LO, valid, no ) | - IO_STATE(R_TLB_LO, kernel,no ) | - IO_STATE(R_TLB_LO, we, no ) | - IO_FIELD(R_TLB_LO, pfn, 0 ) ); - } - } - restore_flags(flags); -} - -/* dump the entire TLB for debug purposes */ - -#if 0 -void -dump_tlb_all(void) -{ - int i; - unsigned long flags; - - printk("TLB dump. LO is: pfn | reserved | global | valid | kernel | we |\n"); - - save_and_cli(flags); - for(i = 0; i < NUM_TLB_ENTRIES; i++) { - *R_TLB_SELECT = ( IO_FIELD(R_TLB_SELECT, index, i) ); - printk("Entry %d: HI 0x%08lx, LO 0x%08lx\n", - i, *R_TLB_HI, *R_TLB_LO); - } - restore_flags(flags); -} -#endif - /* * Initialize the context related info for a new mm_struct * instance. @@ -279,33 +85,6 @@ get_mmu_context(struct mm_struct *mm) alloc_context(mm); } -/* called in schedule() just before actually doing the switch_to */ - -void -switch_mm(struct mm_struct *prev, struct mm_struct *next, - struct task_struct *tsk, int cpu) -{ - /* make sure we have a context */ - - get_mmu_context(next); - - /* remember the pgd for the fault handlers - * this is similar to the pgd register in some other CPU's. - * we need our own copy of it because current and active_mm - * might be invalid at points where we still need to derefer - * the pgd. - */ - - current_pgd = next->pgd; - - /* switch context in the MMU */ - - D(printk("switching mmu_context to %d (%p)\n", next->context, next)); - - *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT, page_id, next->context); -} - - /* called by __exit_mm to destroy the used MMU context if any before * destroying the mm itself. this is only called when the last user of the mm * drops it. |
