From: David Anders Date: Thu, 19 Jan 2006 13:51:23 +0000 (-0000) Subject: add Sharp LH79520 based LNode80 target X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=5d46a9bb79cb1430bbb96311ed94dae0a22f30ac;p=buildroot.git add Sharp LH79520 based LNode80 target --- diff --git a/target/device/Config.in b/target/device/Config.in index 15a9d814f3..8378bff13a 100644 --- a/target/device/Config.in +++ b/target/device/Config.in @@ -4,6 +4,7 @@ source "target/device/Soekris/Config.in" source "target/device/Hitachi/Config.in" source "target/device/AMD/Config.in" source "target/device/Via/Config.in" +source "target/device/Sharp/Config.in" # This must be last source "target/generic/Config.in" diff --git a/target/device/Sharp/Config.in b/target/device/Sharp/Config.in new file mode 100644 index 0000000000..33bf5fa0b2 --- /dev/null +++ b/target/device/Sharp/Config.in @@ -0,0 +1,9 @@ +comment "Sharp Specific Device Support" + depends BR2_arm + +config BR2_TARGET_SHARP_LNODE80 + bool "Board support for the LNode80 development board" + depends BR2_arm + default n + help + Support for the LNode80 LH79520 based device \ No newline at end of file diff --git a/target/device/Sharp/LNode80/Makefile.in b/target/device/Sharp/LNode80/Makefile.in new file mode 100644 index 0000000000..aefc2aef44 --- /dev/null +++ b/target/device/Sharp/LNode80/Makefile.in @@ -0,0 +1,6 @@ +SHARP_LNODE80_PATH=target/device/Sharp/LNode80 + +TARGET_SKEL_DIR=$(SHARP_LNODE80_PATH)/target_skeleton +TARGET_DEVICE_TABLE=$(SHARP_LNODE80_PATH)/device_table.txt + +TARGETS+=linux diff --git a/target/device/Sharp/LNode80/device_table.txt b/target/device/Sharp/LNode80/device_table.txt new file mode 100644 index 0000000000..cfea770dde --- /dev/null +++ b/target/device/Sharp/LNode80/device_table.txt @@ -0,0 +1,172 @@ +# When building a target filesystem, it is desirable to not have to +# become root and then run 'mknod' a thousand times. Using a device +# table you can create device nodes and directories "on the fly". +# +# This is a sample device table file for use with genext2fs. You can +# do all sorts of interesting things with a device table file. For +# example, if you want to adjust the permissions on a particular file +# you can just add an entry like: +# /sbin/foobar f 2755 0 0 - - - - - +# and (assuming the file /sbin/foobar exists) it will be made setuid +# root (regardless of what its permissions are on the host filesystem. +# Furthermore, you can use a single table entry to create a many device +# minors. For example, if I wanted to create /dev/hda and /dev/hda[0-15] +# I could just use the following two table entries: +# /dev/hda b 640 0 0 3 0 0 0 - +# /dev/hda b 640 0 0 3 1 1 1 15 +# +# Device table entries take the form of: +# +# where name is the file name, type can be one of: +# f A regular file +# d Directory +# c Character special device file +# b Block special device file +# p Fifo (named pipe) +# uid is the user id for the target file, gid is the group id for the +# target file. The rest of the entries (major, minor, etc) apply only +# to device special files. + +# Have fun +# -Erik Andersen +# + +# +/dev d 755 0 0 - - - - - +/dev/pts d 755 0 0 - - - - - +/dev/shm d 755 0 0 - - - - - +/tmp d 1777 0 0 - - - - - +/etc d 755 0 0 - - - - - +/home/default d 2755 1000 1000 - - - - - +# +/bin/busybox f 4755 0 0 - - - - - +/etc/shadow f 600 0 0 - - - - - +/etc/passwd f 644 0 0 - - - - - +/etc/network/if-up.d d 755 0 0 - - - - - +/etc/network/if-pre-up.d d 755 0 0 - - - - - +/etc/network/if-down.d d 755 0 0 - - - - - +/etc/network/if-post-down.d d 755 0 0 - - - - - +/usr/share/udhcpc/default.script f 755 0 0 - - - - - +# uncomment this to allow starting x as non-root +#/usr/X11R6/bin/Xfbdev f 4755 0 0 - - - - - +# Normal system devices +/dev/mem c 640 0 0 1 1 0 0 - +/dev/kmem c 640 0 0 1 2 0 0 - +/dev/null c 666 0 0 1 3 0 0 - +/dev/zero c 666 0 0 1 5 0 0 - +/dev/random c 666 0 0 1 8 0 0 - +/dev/urandom c 666 0 0 1 9 0 0 - +/dev/ram b 640 0 0 1 1 0 0 - +/dev/ram b 640 0 0 1 0 0 1 4 +/dev/loop b 640 0 0 7 0 0 1 2 +/dev/rtc c 640 0 0 10 135 - - - +/dev/console c 666 0 0 5 1 - - - +/dev/tty c 666 0 0 5 0 - - - +/dev/tty c 666 0 0 4 0 0 1 8 +/dev/ttyp c 666 0 0 3 0 0 1 10 +/dev/ptyp c 666 0 0 2 0 0 1 10 +/dev/ptmx c 666 0 0 5 2 - - - +/dev/ttyP c 666 0 0 57 0 0 1 4 +/dev/ttyS c 666 0 0 4 64 0 1 4 +/dev/fb c 640 0 5 29 0 0 32 4 +#/dev/ttySA c 666 0 0 204 5 0 1 3 +/dev/psaux c 666 0 0 10 1 0 0 - +#/dev/ppp c 666 0 0 108 0 - - - +/dev/ttyAM c 666 0 0 204 16 0 1 2 + +# Input stuff +/dev/input d 755 0 0 - - - - - +/dev/input/mice c 640 0 0 13 63 0 0 - +/dev/input/mouse c 660 0 0 13 32 0 1 4 +/dev/input/event c 660 0 0 13 64 0 1 4 +#/dev/input/js c 660 0 0 13 0 0 1 4 + + +# MTD stuff +/dev/mtd c 640 0 0 90 0 0 2 4 +/dev/mtdblock b 640 0 0 31 0 0 1 4 + +#Tun/tap driver +/dev/net d 755 0 0 - - - - - +/dev/net/tun c 660 0 0 10 200 - - - + +# Audio stuff +#/dev/audio c 666 0 29 14 4 - - - +#/dev/audio1 c 666 0 29 14 20 - - - +#/dev/dsp c 666 0 29 14 3 - - - +#/dev/dsp1 c 666 0 29 14 19 - - - +#/dev/sndstat c 666 0 29 14 6 - - - + +# User-mode Linux stuff +#/dev/ubda b 640 0 0 98 0 0 0 - +#/dev/ubda b 640 0 0 98 1 1 1 15 + +# IDE Devices +/dev/hda b 640 0 0 3 0 0 0 - +/dev/hda b 640 0 0 3 1 1 1 15 +/dev/hdb b 640 0 0 3 64 0 0 - +/dev/hdb b 640 0 0 3 65 1 1 15 +#/dev/hdc b 640 0 0 22 0 0 0 - +#/dev/hdc b 640 0 0 22 1 1 1 15 +#/dev/hdd b 640 0 0 22 64 0 0 - +#/dev/hdd b 640 0 0 22 65 1 1 15 +#/dev/hde b 640 0 0 33 0 0 0 - +#/dev/hde b 640 0 0 33 1 1 1 15 +#/dev/hdf b 640 0 0 33 64 0 0 - +#/dev/hdf b 640 0 0 33 65 1 1 15 +#/dev/hdg b 640 0 0 34 0 0 0 - +#/dev/hdg b 640 0 0 34 1 1 1 15 +#/dev/hdh b 640 0 0 34 64 0 0 - +#/dev/hdh b 640 0 0 34 65 1 1 15 + +# SCSI Devices +#/dev/sda b 640 0 0 8 0 0 0 - +#/dev/sda b 640 0 0 8 1 1 1 15 +#/dev/sdb b 640 0 0 8 16 0 0 - +#/dev/sdb b 640 0 0 8 17 1 1 15 +#/dev/sdc b 640 0 0 8 32 0 0 - +#/dev/sdc b 640 0 0 8 33 1 1 15 +#/dev/sdd b 640 0 0 8 48 0 0 - +#/dev/sdd b 640 0 0 8 49 1 1 15 +#/dev/sde b 640 0 0 8 64 0 0 - +#/dev/sde b 640 0 0 8 65 1 1 15 +#/dev/sdf b 640 0 0 8 80 0 0 - +#/dev/sdf b 640 0 0 8 81 1 1 15 +#/dev/sdg b 640 0 0 8 96 0 0 - +#/dev/sdg b 640 0 0 8 97 1 1 15 +#/dev/sdh b 640 0 0 8 112 0 0 - +#/dev/sdh b 640 0 0 8 113 1 1 15 +#/dev/sg c 640 0 0 21 0 0 1 15 +#/dev/scd b 640 0 0 11 0 0 1 15 +#/dev/st c 640 0 0 9 0 0 1 8 +#/dev/nst c 640 0 0 9 128 0 1 8 +#/dev/st c 640 0 0 9 32 1 1 4 +#/dev/st c 640 0 0 9 64 1 1 4 +#/dev/st c 640 0 0 9 96 1 1 4 + +# Floppy disk devices +#/dev/fd b 640 0 0 2 0 0 1 2 +#/dev/fd0d360 b 640 0 0 2 4 0 0 - +#/dev/fd1d360 b 640 0 0 2 5 0 0 - +#/dev/fd0h1200 b 640 0 0 2 8 0 0 - +#/dev/fd1h1200 b 640 0 0 2 9 0 0 - +#/dev/fd0u1440 b 640 0 0 2 28 0 0 - +#/dev/fd1u1440 b 640 0 0 2 29 0 0 - +#/dev/fd0u2880 b 640 0 0 2 32 0 0 - +#/dev/fd1u2880 b 640 0 0 2 33 0 0 - + +# All the proprietary cdrom devices in the world +#/dev/aztcd b 640 0 0 29 0 0 0 - +#/dev/bpcd b 640 0 0 41 0 0 0 - +#/dev/capi20 c 640 0 0 68 0 0 1 2 +#/dev/cdu31a b 640 0 0 15 0 0 0 - +#/dev/cdu535 b 640 0 0 24 0 0 0 - +#/dev/cm206cd b 640 0 0 32 0 0 0 - +#/dev/sjcd b 640 0 0 18 0 0 0 - +#/dev/sonycd b 640 0 0 15 0 0 0 - +#/dev/gscd b 640 0 0 16 0 0 0 - +#/dev/sbpcd b 640 0 0 25 0 0 0 - +#/dev/sbpcd b 640 0 0 25 0 0 1 4 +#/dev/mcd b 640 0 0 23 0 0 0 - +#/dev/optcd b 640 0 0 17 0 0 0 - + diff --git a/target/device/Sharp/LNode80/kernel-patches/001-patch-2.4.26-vrs1 b/target/device/Sharp/LNode80/kernel-patches/001-patch-2.4.26-vrs1 new file mode 100644 index 0000000000..1e19599098 --- /dev/null +++ b/target/device/Sharp/LNode80/kernel-patches/001-patch-2.4.26-vrs1 @@ -0,0 +1,89183 @@ +diff -urN linux-2.4.26/Documentation/Configure.help linux-2.4.26-vrs1/Documentation/Configure.help +--- linux-2.4.26/Documentation/Configure.help 2004-04-19 11:44:14.000000000 +0100 ++++ linux-2.4.26-vrs1/Documentation/Configure.help 2004-04-18 21:47:49.000000000 +0100 +@@ -4879,6 +4879,13 @@ + Say Y to enable support for Permedia2 AGP frame buffer card from + 3Dlabs (aka `Graphic Blaster Exxtreme') on the PCI bus. + ++Permedia3 support (EXPERIMENTAL) ++CONFIG_FB_PM3 ++ This is the frame buffer device driver for the 3DLabs Permedia3 ++ chipset, used in Formac ProFormance III, 3DLabs Oxygen VX1 & ++ similar boards, 3DLabs Permedia3 Create!, Appian Jeronimo 2000 ++ and maybe other boards. ++ + Phase5 CVisionPPC/BVisionPPC support + CONFIG_FB_PM2_CVPPC + Say Y to enable support for the Amiga Phase 5 CVisionPPC BVisionPPC +@@ -13213,6 +13220,17 @@ + The module will be called tmspci.o. If you want to compile it + as a module, say M here and read . + ++Altera ether00 support ++CONFIG_ETHER00 ++ This is the driver for Altera's ether00 ethernet mac IP core. Say ++ Y here if you want to build support for this into the kernel. It ++ is also available as a module (say M here) that can be inserted/ ++ removed from the kernel at the same time as the PLD is configured. ++ If this driver is running on an epxa10 development board then it ++ will generate a suitable hw address based on the board serial ++ number (MTD support is required for this). Otherwise you will ++ need to set a suitable hw address using ifconfig. ++ + Generic TMS380 ISA support + CONFIG_TMSISA + This tms380 module supports generic TMS380-based ISA cards. +@@ -15157,6 +15175,16 @@ + support" be compiled as a module for this driver to be used + properly. + ++Altera's uart00 serial driver ++CONFIG_SERIAL_UART00 ++ Say Y here if you want to use the hard logic uart on Excalibur. This ++ driver also supports soft logic implentations of this uart core. ++ ++Serial console on uart00 ++CONFIG_SERIAL_UART00_CONSOLE ++ Say Y here if you want to support a serial console on an Excalibur ++ hard logic uart or uart00 IP core. ++ + USB ConnectTech WhiteHEAT Serial Driver + CONFIG_USB_SERIAL_WHITEHEAT + Say Y here if you want to use a ConnectTech WhiteHEAT 4 port +@@ -19173,6 +19201,20 @@ + . + The module will be called i2c-velleman.o. + ++Guide GPIO adapter ++CONFIG_I2C_GUIDE ++ This supports the Iders GUIDE I2C bit-bashing adapter. If you have ++ selected the GUIDE A07 as your ARM system type, you cannot deselect ++ this option, as it is required for proper operation of the GUIDE. ++ ++ This interface uses /dev/i2c-0 (major 89, minor 0). ++ ++ Say Y if you own such an adapter. ++ ++ This driver is also available as a module. If you want to compile ++ it as a module, say M here and read Documentation/modules.txt. The ++ module will be called i2c-guide.o. ++ + I2C PCF 8584 interfaces + CONFIG_I2C_ALGOPCF + This allows you to use a range of I2C adapters called PCF adapters. +@@ -20310,6 +20352,17 @@ + . The module will be called + softdog.o. + ++SA1100 Internal Watchdog ++CONFIG_SA1100_WATCHDOG ++ Watchdog timer embedded into SA11x0 chips. This will reboot your ++ system when timeout is reached. ++ NOTE, that once enabled, this timer cannot be disabled. ++ ++ This driver is also available as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want). ++ If you want to compile it as a module, say M here and read ++ Documentation/modules.txt. The module will be called sa1100_wdt.o. ++ + Berkshire Products PC Watchdog + CONFIG_PCWATCHDOG + This is the driver for the Berkshire Products PC Watchdog card. +@@ -21971,6 +22024,30 @@ + from RME. If you want to acess advanced features of the card, read + Documentation/sound/rme96xx. + ++Assabet audio (UDA1341) support ++CONFIG_SOUND_ASSABET_UDA1341 ++ Say Y or M if you have an Intel Assabet evaluation board and want to ++ use the Philips UDA 1341 audio chip (the one that drives the stereo ++ audio output) on the SA1100 SSP port. ++ ++Compaq iPAQ audio support ++CONFIG_SOUND_H3600_UDA1341 ++ Say Y or M if you have a Compaq iPaq handheld computer and want to ++ use its Philips UDA 1341 audio chip. ++ ++Audio support for SA1111/UDA1341 ++CONFIG_SOUND_SA1111_UDA1341 ++ Say Y or M if you have an SA11x0 system with a Philips UDA 1341 ++ connected to the SA11x1. An example of such a system is the Intel ++ Assabet evaluation board connected to a Neponset expansion board. ++ ++Generic DAC on the SA11x0 SSP port ++CONFIG_SOUND_SA1100SSP ++ Say Y or M if you have an SA-11x0 system with a DAC on the SSP port. ++ The LART has an Burr-Brown PCM 1710 digital to analog convertor on ++ the SSP port, so you want to say Y or M for the LART. It might work ++ on other SA-1100 platforms, too, but this is not tested. ++ + Are you using a crosscompiler + CONFIG_CROSSCOMPILE + Say Y here if you are compiling the kernel on a different +@@ -25674,6 +25751,20 @@ + Say Y if configuring for a Pangolin. + Say N otherwise. + ++Shannon ++CONFIG_SA1100_SHANNON ++ The Shannon (also known as a Tuxscreen, and also as a IS2630) was a ++ limited edition webphone produced by Philips. The Shannon is a SA1100 ++ platform with a 640x480 LCD, touchscreen, CIR keyboard, PCMCIA slots, ++ and a telco interface. ++ ++Simputer ++CONFIG_SA1100_SIMPUTER ++ Say Y here if you are using an Intel(R) StrongARM(R) SA-1110 ++ based Simputer. See http://www.simputer.org/ for information ++ on the Simputer. The Simputer software is actively maintained ++ by PicoPeta Simputers Pvt. Ltd. (http://www.picopeta.com) ++ + Victor + CONFIG_SA1100_VICTOR + Say Y here if you are using a Visu Aide Intel(R) StrongARM(R) +@@ -25681,6 +25772,14 @@ + for information on + this system. + ++Radisys Corp. Tulsa ++CONFIG_SA1100_PFS168 ++ The Radisys Corp. PFS-168 (aka Tulsa) is an IntelĀ® StrongArmĀ® SA-1110 based ++ computer which includes the SA-1111 Microprocessor Companion Chip and other ++ custom I/O designed to add connectivity and multimedia features for vending ++ and business machine applications. Say Y here if you require support for ++ this target. ++ + # Choice: cerf_ram + Cerf on-board RAM size + CONFIG_SA1100_CERF_8MB +@@ -25748,37 +25847,65 @@ + Say Y if you want support for the ARM920T processor. + Otherwise, say N. + +-Support ARM1020 processor +-CONFIG_CPU_ARM1020 +- The ARM1020 is the cached version of the ARM10 processor, +- with an addition of a floating-point unit. ++Support ARM922T processor ++CONFIG_CPU_ARM922T ++ The ARM922T is a version of the ARM920T, but with smaller ++ instruction and data caches. It is used in Altera's ++ Excalibur XA device family. + +- Say Y if you want support for the ARM1020 processor. ++ Say Y if you want support for the ARM922T processor. + Otherwise, say N. + +-Disable I-Cache ++Disable instruction cache + CONFIG_CPU_ICACHE_DISABLE +- Say Y here to disable the processor instruction cache. Unless +- you have a reason not to or are unsure, say N. ++ Say Y here to disable the processor instruction cache. Unless ++ you have a reason to do this, say N. + +-Disable D-Cache ++Disable data cache + CONFIG_CPU_DCACHE_DISABLE +- Say Y here to disable the processor data cache. Unless +- you have a reason not to or are unsure, say N. ++ Say Y here to disable the processor data cache. Unless ++ you have a reason to do this, say N. + +-Force write through D-cache ++Use data cache in writethrough mode + CONFIG_CPU_DCACHE_WRITETHROUGH +- Say Y here to use the data cache in write-through mode. Unless you +- specifically require this or are unsure, say N. ++ Say Y here to use the data cache in writethough mode. Unless you ++ specifically require this, say N. ++ ++Support ARM1020 processor ++CONFIG_CPU_ARM1020 ++ The ARM1020 is the 32K cached version of the ARM10 processor, ++ with an addition of a floating-point unit. ++ ++ Say Y if you want support for the ARM1020 processor. ++ Otherwise, say N. ++ ++Support ARM1022 processor ++CONFIG_CPU_ARM1022 ++ The ARM1022E is the 16K cached version of the ARM10 processor, ++ with an addition of a floating-point unit. ++ ++ Say Y if you want support for the ARM1022 processor. ++ Otherwise, say N. + +-Round robin I and D cache replacement algorithm ++Force round-robin cache line replacement + CONFIG_CPU_CACHE_ROUND_ROBIN +- Say Y here to use the predictable round-robin cache replacement +- policy. Unless you specifically require this or are unsure, say N. ++ Say Y here to force the caches to use a round-robin ++ algorithm when picking a cache line to evict. Unless you ++ specifically require this, say N. ++ ++Disable the write buffer ++CONFIG_CPU_WB_DISABLE ++ Say Y here to turn off the write buffer (if possible) ++ Unless you specifically require this, say N. Note that ++ not all ARM processors allow the write buffer to be ++ disabled. + + Disable branch prediction + CONFIG_CPU_BPREDICT_DISABLE +- Say Y here to disable branch prediction. If unsure, say N. ++ The ARM10 family of processors support branch prediction, ++ which can significantly speed up execution of loops. ++ Say Y here to disable branch prediction. Unless you ++ specifically require this, say N. + + Compressed boot loader in ROM/flash + CONFIG_ZBOOT_ROM +@@ -25825,6 +25952,11 @@ + Say Y here if you are using the inhand electronics OmniMeter. See + for details. + ++HP Laboratories BadgePAD 4 ++CONFIG_SA1100_BADGE4 ++ Say Y here if you want to build a kernel for the HP Laboratories ++ BadgePAD 4. ++ + Load kernel using Angel Debug Monitor + CONFIG_ANGELBOOT + Say Y if you plan to load the kernel using Angel, ARM Ltd's target +@@ -25837,6 +25969,15 @@ + board includes 2 serial ports, Ethernet, IRDA, and expansion headers. + It comes with 16 MB SDRAM and 8 MB flash ROM. + ++GUIDEA07 ++CONFIG_ARCH_GUIDEA07 ++ Say Y if you are using a GUIDE (A07) board. ++ ++ This board is based on the cs89712 processor and shares much common ++ hardware with the CDB89712 configuration. When you select this ++ option and the CDB89712 becomes enabled also, don't worry. It's ++ supposed to be that way. ++ + CLPS-711X internal ROM bootstrap + CONFIG_EP72XX_ROM_BOOT + If you say Y here, your CLPS711x-based kernel will use the bootstrap +@@ -25865,19 +26006,27 @@ + You may say N here if you are going to load the Acorn FPEmulator + early in the bootup. + ++Math emulation 80-bit support ++CONFIG_FPE_NWFPE_XP ++ Say Y to include 80-bit support in the kernel floating-point ++ emulator. Otherwise, only 32 and 64-bit support is compiled in. ++ Note that gcc does not generate 80-bit operations by default, ++ so in most cases this option only enlarges the size of the ++ floating point emulator without any good reason. ++ ++ You almost surely want to say N here. ++ + FastFPE math emulation + CONFIG_FPE_FASTFPE + Say Y here to include the FAST floating point emulator in the kernel. +- This is an experimental much faster emulator which has only 32 bit ++ This is an experimental much faster emulator which now also has full + precision for the mantissa. It does not support any exceptions. +- This makes it very simple, it is approximately 4-8 times faster than +- NWFPE. ++ It is very simple, and approximately 3-6 times faster than NWFPE. + +- It should be sufficient for most programs. It is definitely not +- suitable if you do scientific calculations that need double +- precision for iteration formulas that sum up lots of very small +- numbers. If you do not feel you need a faster FP emulation you +- should better choose NWFPE. ++ It should be sufficient for most programs. It may be not suitable ++ for scientific calculations, but you have to check this for yourself. ++ If you do not feel you need a faster FP emulation you should better ++ choose NWFPE. + + It is also possible to say M to build the emulator as a module + (fastfpe.o). But keep in mind that you should only load the FP +diff -urN linux-2.4.26/Documentation/arm/Porting linux-2.4.26-vrs1/Documentation/arm/Porting +--- linux-2.4.26/Documentation/arm/Porting 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/Documentation/arm/Porting 2004-02-22 17:29:23.000000000 +0000 +@@ -0,0 +1,135 @@ ++Taken from list archive at http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2001-July/004064.html ++ ++Initial definitions ++------------------- ++ ++The following symbol definitions rely on you knowing the translation that ++__virt_to_phys() does for your machine. This macro converts the passed ++virtual address to a physical address. Normally, it is simply: ++ ++ phys = virt - PAGE_OFFSET + PHYS_OFFSET ++ ++ ++Decompressor Symbols ++-------------------- ++ ++ZTEXTADDR ++ Start address of decompressor. There's no point in talking about ++ virtual or physical addresses here, since the MMU will be off at ++ the time when you call the decompressor code. You normally call ++ the kernel at this address to start it booting. This doesn't have ++ to be located in RAM, it can be in flash or other read-only or ++ read-write addressable medium. ++ ++ZBSSADDR ++ Start address of zero-initialised work area for the decompressor. ++ This must be pointing at RAM. The decompressor will zero initialise ++ this for you. Again, the MMU will be off. ++ ++ZRELADDR ++ This is the address where the decompressed kernel will be written, ++ and eventually executed. The following constraint must be valid: ++ ++ __virt_to_phys(TEXTADDR) == ZRELADDR ++ ++ The initial part of the kernel is carefully coded to be position ++ independent. ++ ++INITRD_PHYS ++ Physical address to place the initial RAM disk. Only relevant if ++ you are using the bootpImage stuff (which only works on the old ++ struct param_struct). ++ ++INITRD_VIRT ++ Virtual address of the initial RAM disk. The following constraint ++ must be valid: ++ ++ __virt_to_phys(INITRD_VIRT) == INITRD_PHYS ++ ++PARAMS_PHYS ++ Physical address of the struct param_struct or tag list, giving the ++ kernel various parameters about its execution environment. ++ ++ ++Kernel Symbols ++-------------- ++ ++PHYS_OFFSET ++ Physical start address of the first bank of RAM. ++ ++PAGE_OFFSET ++ Virtual start address of the first bank of RAM. During the kernel ++ boot phase, virtual address PAGE_OFFSET will be mapped to physical ++ address PHYS_OFFSET, along with any other mappings you supply. ++ This should be the same value as TASK_SIZE. ++ ++TASK_SIZE ++ The maximum size of a user process in bytes. Since user space ++ always starts at zero, this is the maximum address that a user ++ process can access+1. The user space stack grows down from this ++ address. ++ ++ Any virtual address below TASK_SIZE is deemed to be user process ++ area, and therefore managed dynamically on a process by process ++ basis by the kernel. I'll call this the user segment. ++ ++ Anything above TASK_SIZE is common to all processes. I'll call ++ this the kernel segment. ++ ++ (In other words, you can't put IO mappings below TASK_SIZE, and ++ hence PAGE_OFFSET). ++ ++TEXTADDR ++ Virtual start address of kernel, normally PAGE_OFFSET + 0x8000. ++ This is where the kernel image ends up. With the latest kernels, ++ it must be located at 32768 bytes into a 128MB region. Previous ++ kernels placed a restriction of 256MB here. ++ ++DATAADDR ++ Virtual address for the kernel data segment. Must not be defined ++ when using the decompressor. ++ ++VMALLOC_START ++VMALLOC_END ++ Virtual addresses bounding the vmalloc() area. There must not be ++ any static mappings in this area; vmalloc will overwrite them. ++ The addresses must also be in the kernel segment (see above). ++ Normally, the vmalloc() area starts VMALLOC_OFFSET bytes above the ++ last virtual RAM address (found using variable high_memory). ++ ++VMALLOC_OFFSET ++ Offset normally incorporated into VMALLOC_START to provide a hole ++ between virtual RAM and the vmalloc area. We do this to allow ++ out of bounds memory accesses (eg, something writing off the end ++ of the mapped memory map) to be caught. Normally set to 8MB. ++ ++Architecture Specific Macros ++---------------------------- ++ ++BOOT_MEM(pram,pio,vio) ++ `pram' specifies the physical start address of RAM. Must always ++ be present, and should be the same as PHYS_OFFSET. ++ ++ `pio' is the physical address of an 8MB region containing IO for ++ use with the debugging macros in arch/arm/kernel/debug-armv.S. ++ ++ `vio' is the virtual address of the 8MB debugging region. ++ ++ It is expected that the debugging region will be re-initialised ++ by the architecture specific code later in the code (via the ++ MAPIO function). ++ ++BOOT_PARAMS ++ Same as, and see PARAMS_PHYS. ++ ++FIXUP(func) ++ Machine specific fixups, run before memory subsystems have been ++ initialised. ++ ++MAPIO(func) ++ Machine specific function to map IO areas (including the debug ++ region above). ++ ++INITIRQ(func) ++ Machine specific function to initialise interrupts. ++ +diff -urN linux-2.4.26/Documentation/arm/mem_alignment linux-2.4.26-vrs1/Documentation/arm/mem_alignment +--- linux-2.4.26/Documentation/arm/mem_alignment 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/Documentation/arm/mem_alignment 2004-04-09 15:09:44.000000000 +0100 +@@ -0,0 +1,58 @@ ++Too many problems poped up because of unnoticed misaligned memory access in ++kernel code lately. Therefore the alignment fixup is now unconditionally ++configured in for SA11x0 based targets. According to Alan Cox, this is a ++bad idea to configure it out, but Russell King has some good reasons for ++doing so on some f***ed up ARM architectures like the EBSA110. However ++this is not the case on many design I'm aware of, like all SA11x0 based ++ones. ++ ++Of course this is a bad idea to rely on the alignment trap to perform ++unaligned memory access in general. If those access are predictable, you ++are better to use the macros provided by include/asm/unaligned.h. The ++alignment trap can fixup misaligned access for the exception cases, but at ++a high performance cost. It better be rare. ++ ++Now for user space applications, it is possible to configure the alignment ++trap to SIGBUS any code performing unaligned access (good for debugging bad ++code), or even fixup the access by software like for kernel code. The later ++mode isn't recommended for performance reasons (just think about the ++floating point emulation that works about the same way). Fix your code ++instead! ++ ++Please note that randomly changing the behaviour without good thought is ++real bad - it changes the behaviour of all unaligned instructions in user ++space, and might cause programs to fail unexpectedly. ++ ++To change the alignment trap behavior, simply echo a number into ++/proc/cpu/alignment. The number is made up from various bits: ++ ++bit behavior when set ++--- ----------------- ++ ++0 A user process performing an unaligned memory access ++ will cause the kernel to print a message indicating ++ process name, pid, pc, instruction, address, and the ++ fault code. ++ ++1 The kernel will attempt to fix up the user process ++ performing the unaligned access. This is of course ++ slow (think about the floating point emulator) and ++ not recommended for production use. ++ ++2 The kernel will send a SIGBUS signal to the user process ++ performing the unaligned access. ++ ++Note that not all combinations are supported - only values 0 through 5. ++(6 and 7 don't make sense). ++ ++For example, the following will turn on the warnings, but without ++fixing up or sending SIGBUS signals: ++ ++ echo 1 > /proc/cpu/alignment ++ ++You can also read the content of the same file to get statistical ++information on unaligned access occurrences plus the current mode of ++operation for user space code. ++ ++ ++Nicolas Pitre, Mar 13, 2001. Modified Russell King, Nov 30, 2001. +diff -urN linux-2.4.26/Documentation/arm/memory.txt linux-2.4.26-vrs1/Documentation/arm/memory.txt +--- linux-2.4.26/Documentation/arm/memory.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/Documentation/arm/memory.txt 2004-02-22 17:32:57.000000000 +0000 +@@ -0,0 +1,74 @@ ++ Kernel Memory Layout on ARM Linux ++ ++ Russell King ++ April 27, 2003 (2.5.68) ++ ++This document describes the virtual memory layout which the Linux ++kernel uses for ARM processors. It indicates which regions are ++free for platforms to use, and which are used by generic code. ++ ++The ARM CPU is capable of addressing a maximum of 4GB virtual memory ++space, and this must be shared between user space processes, the ++kernel, and hardware devices. ++ ++As the ARM architecture matures, it becomes necessary to reserve ++certain regions of VM space for use for new facilities; therefore ++this document may reserve more VM space over time. ++ ++Start End Use ++-------------------------------------------------------------------------- ++ffff8000 ffffffff copy_user_page / clear_user_page use. ++ For SA11xx and Xscale, this is used to ++ setup a minicache mapping. ++ ++ffff1000 ffff7fff Reserved. ++ Platforms must not use this address range. ++ ++ffff0000 ffff0fff CPU vector page. ++ The CPU vectors are mapped here if the ++ CPU supports vector relocation (control ++ register V bit.) ++ ++ffe00000 fffeffff Free for platform use, not recommended. ++ ++ffc00000 ffdfffff 2MB consistent memory mapping. ++ Memory returned by the consistent_alloc ++ low level function will be dynamically ++ mapped here. ++ ++ff000000 ffbfffff Free for platform use, not recommended. ++ ++VMALLOC_END ff000000 Free for platform use, recommended. ++ ++VMALLOC_START VMALLOC_END vmalloc() / ioremap() space. ++ Memory returned by vmalloc/ioremap will ++ be dynamically placed in this region. ++ VMALLOC_START may be based upon the value ++ of the high_memory variable. ++ ++PAGE_OFFSET high_memory Kernel direct-mapped RAM region. ++ This maps the platforms RAM, and typically ++ maps all platform RAM in a 1:1 relationship. ++ ++TASK_SIZE PAGE_OFFSET Kernel module space ++ Kernel modules inserted via insmod are ++ placed here using dynamic mappings. ++ ++00001000 TASK_SIZE User space mappings ++ Per-thread mappings are placed here via ++ the mmap() system call. ++ ++00000000 00000fff CPU vector page / null pointer trap ++ CPUs which do not support vector remapping ++ place their vector page here. NULL pointer ++ dereferences by both the kernel and user ++ space are also caught via this mapping. ++ ++Please note that mappings which collide with the above areas may result ++in a non-bootable kernel, or may cause the kernel to (eventually) panic ++at run time. ++ ++Since future CPUs may impact the kernel mapping layout, user programs ++must not access any memory which is not mapped inside their 0x0001000 ++to TASK_SIZE address range. If they wish to access these areas, they ++must set up their own mappings using open() and mmap(). +diff -urN linux-2.4.26/Documentation/cpufreq/core.txt linux-2.4.26-vrs1/Documentation/cpufreq/core.txt +--- linux-2.4.26/Documentation/cpufreq/core.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/Documentation/cpufreq/core.txt 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,94 @@ ++ CPU frequency and voltage scaling code in the Linux(TM) kernel ++ ++ ++ L i n u x C P U F r e q ++ ++ C P U F r e q C o r e ++ ++ ++ Dominik Brodowski ++ David Kimdon ++ ++ ++ ++ Clock scaling allows you to change the clock speed of the CPUs on the ++ fly. This is a nice method to save battery power, because the lower ++ the clock speed, the less power the CPU consumes. ++ ++ ++Contents: ++--------- ++1. CPUFreq core and interfaces ++2. CPUFreq notifiers ++ ++1. General Information ++======================= ++ ++The CPUFreq core code is located in linux/kernel/cpufreq.c. This ++cpufreq code offers a standardized interface for the CPUFreq ++architecture drivers (those pieces of code that do actual ++frequency transitions), as well as to "notifiers". These are device ++drivers or other part of the kernel that need to be informed of ++policy changes (ex. thermal modules like ACPI) or of all ++frequency changes (ex. timing code) or even need to force certain ++speed limits (like LCD drivers on ARM architecture). Additionally, the ++kernel "constant" loops_per_jiffy is updated on frequency changes ++here. ++ ++Reference counting is done by cpufreq_get_cpu and cpufreq_put_cpu, ++which make sure that the cpufreq processor driver is correctly ++registered with the core, and will not be unloaded until ++cpufreq_put_cpu is called. ++ ++2. CPUFreq notifiers ++==================== ++ ++CPUFreq notifiers conform to the standard kernel notifier interface. ++See linux/include/linux/notifier.h for details on notifiers. ++ ++There are two different CPUFreq notifiers - policy notifiers and ++transition notifiers. ++ ++ ++2.1 CPUFreq policy notifiers ++---------------------------- ++ ++These are notified when a new policy is intended to be set. Each ++CPUFreq policy notifier is called three times for a policy transition: ++ ++1.) During CPUFREQ_ADJUST all CPUFreq notifiers may change the limit if ++ they see a need for this - may it be thermal considerations or ++ hardware limitations. ++ ++2.) During CPUFREQ_INCOMPATIBLE only changes may be done in order to avoid ++ hardware failure. ++ ++3.) And during CPUFREQ_NOTIFY all notifiers are informed of the new policy ++ - if two hardware drivers failed to agree on a new policy before this ++ stage, the incompatible hardware shall be shut down, and the user ++ informed of this. ++ ++The phase is specified in the second argument to the notifier. ++ ++The third argument, a void *pointer, points to a struct cpufreq_policy ++consisting of five values: cpu, min, max, policy and max_cpu_freq. min ++and max are the lower and upper frequencies (in kHz) of the new ++policy, policy the new policy, cpu the number of the affected CPU or ++CPUFREQ_ALL_CPUS for all CPUs; and max_cpu_freq the maximum supported ++CPU frequency. This value is given for informational purposes only. ++ ++ ++2.2 CPUFreq transition notifiers ++-------------------------------- ++ ++These are notified twice when the CPUfreq driver switches the CPU core ++frequency and this change has any external implications. ++ ++The second argument specifies the phase - CPUFREQ_PRECHANGE or ++CPUFREQ_POSTCHANGE. ++ ++The third argument is a struct cpufreq_freqs with the following ++values: ++cpu - number of the affected CPU or CPUFREQ_ALL_CPUS ++old - old frequency ++new - new frequency +diff -urN linux-2.4.26/Documentation/cpufreq/cpu-drivers.txt linux-2.4.26-vrs1/Documentation/cpufreq/cpu-drivers.txt +--- linux-2.4.26/Documentation/cpufreq/cpu-drivers.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/Documentation/cpufreq/cpu-drivers.txt 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,210 @@ ++ CPU frequency and voltage scaling code in the Linux(TM) kernel ++ ++ ++ L i n u x C P U F r e q ++ ++ C P U D r i v e r s ++ ++ - information for developers - ++ ++ ++ Dominik Brodowski ++ ++ ++ ++ Clock scaling allows you to change the clock speed of the CPUs on the ++ fly. This is a nice method to save battery power, because the lower ++ the clock speed, the less power the CPU consumes. ++ ++ ++Contents: ++--------- ++1. What To Do? ++1.1 Initialization ++1.2 Per-CPU Initialization ++1.3 verify ++1.4 target or setpolicy? ++1.5 target ++1.6 setpolicy ++2. Frequency Table Helpers ++ ++ ++ ++1. What To Do? ++============== ++ ++So, you just got a brand-new CPU / chipset with datasheets and want to ++add cpufreq support for this CPU / chipset? Great. Here are some hints ++on what is neccessary: ++ ++ ++1.1 Initialization ++------------------ ++ ++First of all, in an __initcall level 7 or later (preferrably ++module_init() so that your driver is modularized) function check ++whether this kernel runs on the right CPU and the right chipset. If ++so, register a struct cpufreq_driver with the CPUfreq core using ++cpufreq_register_driver() ++ ++What shall this struct cpufreq_driver contain? ++ ++cpufreq_driver.name - The name of this driver. ++ ++cpufreq_driver.init - A pointer to the per-CPU initialization ++ function. ++ ++cpufreq_driver.verify - A pointer to a "verfication" funciton. ++ ++cpufreq_driver.setpolicy _or_ ++cpufreq_driver.target - See below on the differences. ++ ++And optionally ++ ++cpufreq_driver.exit - A pointer to a per-CPU cleanup function. ++ ++cpufreq_driver.attr - A pointer to a NULL-terminated list of ++ "struct freq_attr" which allow to ++ export values to sysfs. ++ ++ ++1.2 Per-CPU Initialization ++-------------------------- ++ ++Whenever a new CPU is registered with the device model, or after the ++cpufreq driver registers itself, the per-CPU initialization fucntion ++cpufreq_driver.init is called. It takes a struct cpufreq_policy ++*policy as argument. What to do now? ++ ++If necessary, activate the CPUfreq support on your CPU (unlock that ++register etc.). ++ ++Then, the driver must fill in the following values: ++ ++policy->cpuinfo.min_freq _and_ ++policy->cpuinfo.max_freq - the minimum and maximum frequency ++ (in kHz) which is supported by ++ this CPU ++policy->cpuinfo.transition_latency the time it takes on this CPU to ++ switch between two frequencies (if ++ appropriate, else specify ++ CPUFREQ_ETERNAL) ++ ++policy->cur The current operating frequency of ++ this CPU (if appropriate) ++policy->min, ++policy->max, ++policy->policy and, if neccessary, ++policy->governor must contain the "default policy" for ++ this CPU. A few moments later, ++ cpufreq_driver.verify and either ++ cpufreq_driver.setpolicy or ++ cpufreq_driver.target is called with ++ these values. ++ ++For setting some of these values, the frequency table helpers might be ++helpful. See the section 2 for more information on them. ++ ++ ++1.3 verify ++------------ ++ ++When the user decides a new policy (consisting of ++"policy,governor,min,max") shall be set, this policy must be validated ++so that incompatible values can be corrected. For verifying these ++values, a frequency table helper and/or the ++cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned ++int min_freq, unsigned int max_freq) function might be helpful. See ++section 2 for details on frequency table helpers. ++ ++You need to make sure that at least one valid frequency (or operating ++range) is within policy->min and policy->max. If necessary, increase ++policy->max fist, and only if this is no solution, decreas policy->min. ++ ++ ++1.4 target or setpolicy? ++---------------------------- ++ ++Most cpufreq drivers or even most cpu frequency scaling algorithms ++only allow the CPU to be set to one frequency. For these, you use the ++->target call. ++ ++Some cpufreq-capable processors switch the frequency between certain ++limits on their own. These shall use the ->setpolicy call ++ ++ ++1.4. target ++------------- ++ ++The target call has three arguments: struct cpufreq_policy *policy, ++unsigned int target_frequency, unsigned int relation. ++ ++The CPUfreq driver must set the new frequency when called here. The ++actual frequency must be determined using the following rules: ++ ++- keep close to "target_freq" ++- policy->min <= new_freq <= policy->max (THIS MUST BE VALID!!!) ++- if relation==CPUFREQ_REL_L, try to select a new_freq higher than or equal ++ target_freq. ("L for lowest, but no lower than") ++- if relation==CPUFREQ_REL_H, try to select a new_freq lower than or equal ++ target_freq. ("H for highest, but no higher than") ++ ++Here again the frequency table helper might assist you - see section 3 ++for details. ++ ++ ++1.5 setpolicy ++--------------- ++ ++The setpolicy call only takes a struct cpufreq_policy *policy as ++argument. You need to set the lower limit of the in-processor or ++in-chipset dynamic frequency switching to policy->min, the upper limit ++to policy->max, and -if supported- select a performance-oriented ++setting when policy->policy is CPUFREQ_POLICY_PERFORMANCE, and a ++powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check ++the reference implementation in arch/i386/kernel/cpu/cpufreq/longrun.c ++ ++ ++ ++2. Frequency Table Helpers ++========================== ++ ++As most cpufreq processors only allow for being set to a few specific ++frequencies, a "frequency table" with some functions might assist in ++some work of the processor driver. Such a "frequency table" consists ++of an array of struct cpufreq_freq_table entries, with any value in ++"index" you want to use, and the corresponding frequency in ++"frequency". At the end of the table, you need to add a ++cpufreq_freq_table entry with frequency set to CPUFREQ_TABLE_END. And ++if you want to skip one entry in the table, set the frequency to ++CPUFREQ_ENTRY_INVALID. The entries don't need to be in ascending ++order. ++ ++By calling cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, ++ struct cpufreq_frequency_table *table); ++the cpuinfo.min_freq and cpuinfo.max_freq values are detected, and ++policy->min and policy->max are set to the same values. This is ++helpful for the per-CPU initialization stage. ++ ++int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, ++ struct cpufreq_frequency_table *table); ++assures that at least one valid frequency is within policy->min and ++policy->max, and all other criteria are met. This is helpful for the ++->verify call. ++ ++int cpufreq_frequency_table_target(struct cpufreq_policy *policy, ++ struct cpufreq_frequency_table *table, ++ unsigned int target_freq, ++ unsigned int relation, ++ unsigned int *index); ++ ++is the corresponding frequency table helper for the ->target ++stage. Just pass the values to this function, and the unsigned int ++index returns the number of the frequency table entry which contains ++the frequency the CPU shall be set to. PLEASE NOTE: This is not the ++"index" which is in this cpufreq_table_entry.index, but instead ++cpufreq_table[index]. So, the new frequency is ++cpufreq_table[index].frequency, and the value you stored into the ++frequency table "index" field is ++cpufreq_table[index].index. ++ +diff -urN linux-2.4.26/Documentation/cpufreq/governors.txt linux-2.4.26-vrs1/Documentation/cpufreq/governors.txt +--- linux-2.4.26/Documentation/cpufreq/governors.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/Documentation/cpufreq/governors.txt 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,155 @@ ++ CPU frequency and voltage scaling code in the Linux(TM) kernel ++ ++ ++ L i n u x C P U F r e q ++ ++ C P U F r e q G o v e r n o r s ++ ++ - information for users and developers - ++ ++ ++ Dominik Brodowski ++ ++ ++ ++ Clock scaling allows you to change the clock speed of the CPUs on the ++ fly. This is a nice method to save battery power, because the lower ++ the clock speed, the less power the CPU consumes. ++ ++ ++Contents: ++--------- ++1. What is a CPUFreq Governor? ++ ++2. Governors In the Linux Kernel ++2.1 Performance ++2.2 Powersave ++2.3 Userspace ++ ++3. The Governor Interface in the CPUfreq Core ++ ++ ++ ++1. What Is A CPUFreq Governor? ++============================== ++ ++Most cpufreq drivers (in fact, all except one, longrun) or even most ++cpu frequency scaling algorithms only offer the CPU to be set to one ++frequency. In order to offer dynamic frequency scaling, the cpufreq ++core must be able to tell these drivers of a "target frequency". So ++these specific drivers will be transformed to offer a "->target" ++call instead of the existing "->setpolicy" call. For "longrun", all ++stays the same, though. ++ ++How to decide what frequency within the CPUfreq policy should be used? ++That's done using "cpufreq governors". Two are already in this patch ++-- they're the already existing "powersave" and "performance" which ++set the frequency statically to the lowest or highest frequency, ++respectively. At least two more such governors will be ready for ++addition in the near future, but likely many more as there are various ++different theories and models about dynamic frequency scaling ++around. Using such a generic interface as cpufreq offers to scaling ++governors, these can be tested extensively, and the best one can be ++selected for each specific use. ++ ++Basically, it's the following flow graph: ++ ++CPU can be set to switch independetly | CPU can only be set ++ within specific "limits" | to specific frequencies ++ ++ "CPUfreq policy" ++ consists of frequency limits (policy->{min,max}) ++ and CPUfreq governor to be used ++ / \ ++ / \ ++ / the cpufreq governor decides ++ / (dynamically or statically) ++ / what target_freq to set within ++ / the limits of policy->{min,max} ++ / \ ++ / \ ++ Using the ->setpolicy call, Using the ->target call, ++ the limits and the the frequency closest ++ "policy" is set. to target_freq is set. ++ It is assured that it ++ is within policy->{min,max} ++ ++ ++2. Governors In the Linux Kernel ++================================ ++ ++2.1 Performance ++--------------- ++ ++The CPUfreq governor "performance" sets the CPU statically to the ++highest frequency within the borders of scaling_min_freq and ++scaling_max_freq. ++ ++ ++2.1 Powersave ++------------- ++ ++The CPUfreq governor "powersave" sets the CPU statically to the ++lowest frequency within the borders of scaling_min_freq and ++scaling_max_freq. ++ ++ ++2.2 Userspace ++------------- ++ ++The CPUfreq governor "userspace" allows the user, or any userspace ++program running with UID "root", to set the CPU to a specifc frequency ++by making a sysfs file "scaling_setspeed" available in the CPU-device ++directory. ++ ++ ++ ++3. The Governor Interface in the CPUfreq Core ++============================================= ++ ++A new governor must register itself with the CPUfreq core using ++"cpufreq_register_governor". The struct cpufreq_governor, which has to ++be passed to that function, must contain the following values: ++ ++governor->name - A unique name for this governor ++governor->governor - The governor callback function ++governor->owner - .THIS_MODULE for the governor module (if ++ appropriate) ++ ++The governor->governor callback is called with the current (or to-be-set) ++cpufreq_policy struct for that CPU, and an unsigned int event. The ++following events are currently defined: ++ ++CPUFREQ_GOV_START: This governor shall start its duty for the CPU ++ policy->cpu ++CPUFREQ_GOV_STOP: This governor shall end its duty for the CPU ++ policy->cpu ++CPUFREQ_GOV_LIMITS: The limits for CPU policy->cpu have changed to ++ policy->min and policy->max. ++ ++If you need other "events" externally of your driver, _only_ use the ++cpufreq_governor_l(unsigned int cpu, unsigned int event) call to the ++CPUfreq core to ensure proper locking. ++ ++ ++The CPUfreq governor may call the CPU processor driver using one of ++these two functions: ++ ++inline int cpufreq_driver_target(struct cpufreq_policy *policy, ++ unsigned int target_freq, ++ unsigned int relation); ++ ++inline int cpufreq_driver_target_l(struct cpufreq_policy *policy, ++ unsigned int target_freq, ++ unsigned int relation); ++ ++target_freq must be within policy->min and policy->max, of course. ++What's the difference between these two functions? When your governor ++still is in a direct code path of a call to governor->governor, the ++cpufreq_driver_sem lock is still held in the cpufreq core, and there's ++no need to lock it again (in fact, this would cause a deadlock). So ++use cpufreq_driver_target only in these cases. In all other cases (for ++example, when there's a "daemonized" function that wakes up every ++second), use cpufreq_driver_target_l to lock the cpufreq_driver_sem ++before the command is passed to the cpufreq processor driver. ++ +diff -urN linux-2.4.26/Documentation/cpufreq/index.txt linux-2.4.26-vrs1/Documentation/cpufreq/index.txt +--- linux-2.4.26/Documentation/cpufreq/index.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/Documentation/cpufreq/index.txt 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,56 @@ ++ CPU frequency and voltage scaling code in the Linux(TM) kernel ++ ++ ++ L i n u x C P U F r e q ++ ++ ++ ++ ++ Dominik Brodowski ++ ++ ++ ++ Clock scaling allows you to change the clock speed of the CPUs on the ++ fly. This is a nice method to save battery power, because the lower ++ the clock speed, the less power the CPU consumes. ++ ++ ++ ++Documents in this directory: ++---------------------------- ++core.txt - General description of the CPUFreq core and ++ of CPUFreq notifiers ++ ++cpu-drivers.txt - How to implement a new cpufreq processor driver ++ ++governors.txt - What are cpufreq governors and how to ++ implement them? ++ ++index.txt - File index, Mailing list and Links (this document) ++ ++user-guide.txt - User Guide to CPUFreq ++ ++ ++Mailing List ++------------ ++There is a CPU frequency changing CVS commit and general list where ++you can report bugs, problems or submit patches. To post a message, ++send an email to cpufreq@www.linux.org.uk, to subscribe go to ++http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the ++mailing list are available to subscribers at ++http://www.linux.org.uk/mailman/private/cpufreq/. ++ ++ ++Links ++----- ++the FTP archives: ++* ftp://ftp.linux.org.uk/pub/linux/cpufreq/ ++ ++how to access the CVS repository: ++* http://cvs.arm.linux.org.uk/ ++ ++the CPUFreq Mailing list: ++* http://www.linux.org.uk/mailman/listinfo/cpufreq ++ ++Clock and voltage scaling for the SA-1100: ++* http://www.lart.tudelft.nl/projects/scaling +diff -urN linux-2.4.26/Documentation/cpufreq/user-guide.txt linux-2.4.26-vrs1/Documentation/cpufreq/user-guide.txt +--- linux-2.4.26/Documentation/cpufreq/user-guide.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/Documentation/cpufreq/user-guide.txt 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,166 @@ ++ CPU frequency and voltage scaling code in the Linux(TM) kernel ++ ++ ++ L i n u x C P U F r e q ++ ++ U S E R G U I D E ++ ++ ++ Dominik Brodowski ++ ++ ++ ++ Clock scaling allows you to change the clock speed of the CPUs on the ++ fly. This is a nice method to save battery power, because the lower ++ the clock speed, the less power the CPU consumes. ++ ++ ++Contents: ++--------- ++1. Supported Architectures and Processors ++1.1 ARM ++1.2 x86 ++1.3 sparc64 ++ ++2. "Policy" / "Governor"? ++2.1 Policy ++2.2 Governor ++ ++3. How to change the CPU cpufreq policy and/or speed ++3.1 Preferred interface: sysfs ++3.2 Deprecated interfaces ++ ++ ++ ++1. Supported Architectures and Processors ++========================================= ++ ++1.1 ARM ++------- ++ ++The following ARM processors are supported by cpufreq: ++ ++ARM Integrator ++ARM-SA1100 ++ARM-SA1110 ++ ++ ++1.2 x86 ++------- ++ ++The following processors for the x86 architecture are supported by cpufreq: ++ ++AMD Elan - SC400, SC410 ++AMD mobile K6-2+ ++AMD mobile K6-3+ ++Cyrix Media GXm ++Intel mobile PIII [*] and Intel mobile PIII-M on certain chipsets ++Intel Pentium 4, Intel Xeon ++National Semiconductors Geode GX ++Transmeta Crusoe ++varios processors on some ACPI 2.0-compatible systems [**] ++ ++[*] only certain Intel mobile PIII processors are supported. If you ++know that you own a speedstep-capable processor, pass the option ++"speedstep_coppermine=1" to the module speedstep.o ++ ++[**] Only if "ACPI Processor Performance States" are available ++to the ACPI<->BIOS interface. ++ ++ ++1.3 sparc64 ++----------- ++ ++The following processors for the sparc64 architecture are supported by ++cpufreq: ++ ++UltraSPARC-III ++ ++ ++ ++2. "Policy" / "Governor" ? ++========================== ++ ++Some CPU frequency scaling-capable processor switch between varios ++frequencies and operating voltages "on the fly" without any kernel or ++user involvement. This guarantuees very fast switching to a frequency ++which is high enough to serve the user's needs, but low enough to save ++power. ++ ++ ++2.1 Policy ++---------- ++ ++On these systems, all you can do is select the lower and upper ++frequency limit as well as whether you want more aggressive ++power-saving or more instantly avaialble processing power. ++ ++ ++2.2 Governor ++------------ ++ ++On all other cpufreq implementations, these boundaries still need to ++be set. Then, a "governor" must be selected. Such a "governor" decides ++what speed the processor shall run within the boundaries. One such ++"governor" is the "userspace" governor. This one allows the user - or ++a yet-to-implement userspace program - to decide what specific speed ++the processor shall run at. ++ ++ ++3. How to change the CPU cpufreq policy and/or speed ++==================================================== ++ ++3.1 Preferred Interface: sysfs ++------------------------------ ++ ++The preferred interface is located in the sysfs filesystem. If you ++mounted it at /sys, the cpufreq interface is located in a subdirectory ++"cpufreq" within the cpu-device directory ++(e.g. /sys/devices/sys/cpu0/cpufreq/ for the first CPU). ++ ++cpuinfo_min_freq : this file shows the minimum operating ++ frequency the processor can run at(in kHz) ++cpuinfo_max_freq : this file shows the maximum operating ++ frequency the processor can run at(in kHz) ++scaling_driver : this file shows what cpufreq driver is ++ used to set the frequency on this CPU ++ ++scaling_available_governors : this file shows the CPUfreq governors ++ available in this kernel. You can see the ++ currently activated governor in ++ ++scaling_governor, and by "echoing" the name of another ++ governor you can change it. Please note ++ that some governors won't load - they only ++ work on some specific architectures or ++ processors. ++scaling_min_freq and ++scaling_max_freq show the current "policy limits" (in ++ kHz). By echoing new values into these ++ files, you can change these limits. ++ ++ ++If you have selected the "userspace" governor which allows you to ++set the CPU operating frequency to a specific value, you can read out ++the current frequency in ++ ++scaling_setspeed. By "echoing" a new frequency into this ++ you can change the speed of the CPU, ++ but only within the limits of ++ scaling_min_freq and scaling_max_freq. ++ ++ ++3.2 Deprecated Interfaces ++------------------------- ++ ++Depending on your kernel configuration, you might find the following ++cpufreq-related files: ++/proc/cpufreq ++/proc/sys/cpu/*/speed ++/proc/sys/cpu/*/speed-min ++/proc/sys/cpu/*/speed-max ++ ++These are files for deprecated interfaces to cpufreq, which offer far ++less functionality. Because of this, these interfaces aren't described ++here. ++ +diff -urN linux-2.4.26/Documentation/cpufreq-old linux-2.4.26-vrs1/Documentation/cpufreq-old +--- linux-2.4.26/Documentation/cpufreq-old 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/Documentation/cpufreq-old 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,332 @@ ++ CPU frequency and voltage scaling code in the Linux(TM) kernel ++ ++ ++ L i n u x C P U F r e q ++ ++ ++ ++ ++ Dominik Brodowski ++ ++ ++ ++ Clock scaling allows you to change the clock speed of the CPUs on the ++ fly. This is a nice method to save battery power, because the lower ++ the clock speed, the less power the CPU consumes. ++ ++ ++ ++Contents: ++--------- ++1. Supported architectures ++2. User interface ++2.1 Sample script for command line interface ++3. CPUFreq core and interfaces ++3.1 General information ++3.2 CPUFreq notifiers ++3.3 CPUFreq architecture drivers ++4. Mailing list and Links ++ ++ ++ ++1. Supported architectures ++========================== ++ ++Some architectures detect the lowest and highest possible speed ++settings, while others rely on user information on this. For the ++latter, a boot parameter is required, for the former, you can specify ++one to set the limits between speed settings may occur. ++The boot parameter has the following syntax: ++ ++ cpufreq=minspeed-maxspeed ++ ++with both minspeed and maxspeed being given in kHz. To set the lower ++limit to 59 MHz and the upper limit to 221 MHz, specify: ++ ++ cpufreq=59000-221000 ++ ++Check the "Speed Limits Detection" information below on whether ++the driver detects the lowest and highest allowed speed setting ++automatically. ++ ++ ++ARM Integrator: ++ SA 1100, SA1110 ++-------------------------------- ++ Speed Limits Detection: On Integrators, the minimum speed is set ++ and the maximum speed has to be specified using the boot ++ parameter. On SA11x0s, the frequencies are fixed (59 - 287 MHz) ++ ++ ++AMD Elan: ++ SC400, SC410 ++-------------------------------- ++ Speed Limits Detection: Not implemented. You need to specify the ++ minimum and maximum frequency in the boot parameter (see above). ++ ++ ++VIA Cyrix Longhaul: ++ VIA Samuel/CyrixIII, VIA Cyrix Samuel/C3, ++ VIA Cyrix Ezra, VIA Cyrix Ezra-T ++-------------------------------- ++ Speed Limits Detection: working. No need for boot parameters. ++ NOTE: Support for certain processors is currently disabled, ++ waiting on updated docs from VIA. ++ ++ ++Intel SpeedStep: ++ certain mobile Intel Pentium III (Coppermine), and all mobile ++ Intel Pentium III-M (Tulatin) and mobile Intel Pentium 4 P4-Ms. ++-------------------------------- ++ Speed Limits Detection: working. No need for boot parameters. ++ NOTE: ++ 1.) mobile Intel Pentium III (Coppermine): ++ The SpeedStep interface may only be used on SpeedStep ++ capable processors. Unforunately, due to lack of documentation, ++ such detection is not yet possible on mobile Intel PIII ++ (Coppermine) processors. In order to activate SpeedStep on such a ++ processor, you have to remove one line manually in ++ linux/drivers/arch/i386/speedstep.c ++ ++ ++P4 CPU Clock Modulation: ++ Intel Pentium 4 Xeon processors ++-------------------------------- ++ Speed Limits Detection: Not implemented. You need to specify the ++ minimum and maximum frequency in the boot parameter (see above). ++ ++ ++ ++2. User Interface ++================= ++ ++CPUFreq uses a "sysctl" interface which is located in ++ /proc/sys/cpu/0/ on UP (uniprocessor) kernels, or ++ /proc/sys/cpu/any/ on SMP (symmetric multiprocessoring) kernels. ++ ++ ++In this directory, you will find three files of importance for ++CPUFreq: speed-max, speed-min, and speed: ++ ++speed shows the current CPU frequency in kHz, ++speed-min the minimal supported CPU frequency, and ++speed-max the maximal supported CPU frequency. ++ ++Please note that you might have to specify these limits as a boot ++parameter depending on the architecture (see above). ++ ++ ++To change the CPU frequency, "echo" the desired CPU frequency (in kHz) ++to speed. For example, to set the CPU speed to the lowest/highest ++allowed frequency do: ++ ++root@notebook:# cat /proc/sys/cpu/0/speed-min > /proc/sys/cpu/0/speed ++root@notebook:# cat /proc/sys/cpu/0/speed-max > /proc/sys/cpu/0/speed ++ ++ ++2.1 Sample script for command line interface ++********************************************** ++ ++ ++Michael Ossmann has written a small command line ++interface for the infinitely lazy. ++ ++#!/bin/bash ++# ++# /usr/local/bin/freq ++# simple command line interface to cpufreq ++ ++[ -n "$1" ] && case "$1" in ++ "min" ) ++ # set frequency to minimum ++ cat /proc/sys/cpu/0/speed-min >/proc/sys/cpu/0/speed ++ ;; ++ "max" ) ++ # set frequency to maximum ++ cat /proc/sys/cpu/0/speed-max >/proc/sys/cpu/0/speed ++ ;; ++ * ) ++ echo "Usage: $0 [min|max]" ++ echo " min: set frequency to minimum and display new frequency" ++ echo " max: set frequency to maximum and display new frequency" ++ echo " no options: display current frequency" ++ exit 1 ++ ;; ++esac ++ ++# display current frequency ++cat /proc/sys/cpu/0/speed ++exit 0 ++ ++ ++ ++3. CPUFreq core and interfaces ++=============================== ++ ++3.1 General information ++************************* ++ ++The CPUFreq core code is located in linux/kernel/cpufreq.c. This ++cpufreq code offers a standardized interface for the CPUFreq ++architecture drivers (those pieces of code that do the actual ++frequency transition), as well as to "notifiers". These are device ++drivers or other part of the kernel that need to be informed of ++frequency changes (like timing code) or even need to force certain ++speed limits (like LCD drivers on ARM architecture). Aditionally, the ++kernel "constant" loops_per_jiffy is updated on frequency changes ++here. ++ ++ ++3.2 CPUFreq notifiers ++*********************** ++ ++CPUFreq notifiers are kernel code that need to be called to either ++a) define certain minimum or maximum speed settings, ++b) be informed of frequency changes in advance of the transition, or ++c) be informed of frequency changes directly after the transition. ++ ++A standard kernel notifier interface is offered for this. See ++linux/include/linux/notifier.h for details on notifiers. ++ ++ ++Data and value passed to CPUFreq notifiers ++------------------------------------------ ++The second argument passed to any notifier is an unsigned int stating ++the phase of the transition: ++CPUFREQ_MINMAX during the process of determing a valid new CPU ++ frequency, ++CPUFREQ_PRECHANGE right before the transition, and ++CPUFREQ_POSTCHANGE right after the transition. ++ ++The third argument, a void *pointer, points to a struct ++cpufreq_freqs. This consists of four values: min, max, cur and new. ++ ++min and max are the current speed limits. Please note: Never update ++these values directly, use cpufreq_updateminmax(struct cpufreq_freqs ++*freqs, unsigned int min, unsigned int max) instead. cur is the ++current/old speed, and new is the new speed, but might only be valid ++on CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE. ++ ++Each notifier gets called all three times on any transition: ++ ++CPUFREQ_MINMAX ++Here the notifier is supposed to update the min and max values to the ++limits the protected device / kernel code needs. As stated above, ++always use cpufreq_updateminmax for this. ++ ++CPUFREQ_PRECHANGE ++CPUFREQ_POSTCHANGE ++Here the notifier is supposed to update all internal (e.g. device ++driver) code which is dependend on the CPU frequency. ++ ++ ++3.3 CPUFreq architecture drivers ++********************************** ++ ++CPUFreq architecture drivers are the pieces of kernel code that ++actually perform CPU frequency transitions. These need to be ++initialised seperately (seperate initcalls), and may be ++modularized. They interact with the CPUFreq core in the following way: ++ ++ ++cpufreq_register() ++------------------ ++cpufreq_register registers an arch driver to the CPUFreq core. Please ++note that only one arch driver may be registered at any time, -EBUSY ++is returned when an arch driver is already registered. The argument to ++cpufreq_register, cpufreq_driver_t driver, is described later. ++ ++ ++cpufreq_unregister() ++-------------------- ++cpufreq_unregister unregisters an arch driver, e.g. on module ++unloading. Please note that there is no check done that this is called ++from the driver which actually registered itself to the core, so ++please only call this function when you are sure the arch driver got ++registered correctly before. ++ ++ ++struct cpufreq_driver ++---------------- ++On initialisation, the arch driver is supposed to pass the following ++entries in struct cpufreq_driver cpufreq_driver: ++ ++cpufreq_verify_t validate: This is a pointer to a function with the ++following definition: ++ unsigned int validating_function (unsigned int kHz). ++It is called right before a transition occurs. The proposed new ++speed setting is passed as an argument in kHz; the validating code ++should verify this is a valid speed setting which is currently ++supported by the CPU. It shall return the closest valid CPU frequency ++in kHz. ++ ++cpufreq_setspeed_t setspeed: This is a pointer to a function with the ++following definition: ++ void setspeed_function (unsigned int kHz). ++This function shall perform the transition to the new CPU frequency ++given as argument in kHz. Note that this argument is exactly the same ++as the one returned by cpufreq_verify_t validate. ++ ++ ++unsigned int freq.cur: The current CPU core frequency. Note that this ++is a requirement while the next two entries are optional. ++ ++ ++unsigned int freq.min (optional): The minimal CPU core frequency this ++CPU supports. This value may be limited further by the ++cpufreq_verify_t validate function, and so this value should be the ++minimal core frequency allowed "theoretically" on this system in this ++configuration. ++ ++ ++unsigned int freq.max (optional): The maximum CPU core frequency this ++CPU supports. This value may be limited further by the ++cpufreq_verify_t validate function, and so this value should be the ++maximum core frequency allowed "theoretically" on this system in this ++configuration. ++ ++ ++Some Requirements to CPUFreq architecture drivers ++------------------------------------------------- ++* Only call cpufreq_register() when the ability to switch CPU ++ frequencies is _verified_ or can't be missing ++* cpufreq_unregister() may only be called if cpufreq_register() has ++ been successfully(!) called before ++* All CPUs have to be set to the same speed whenever setspeed() is ++ called ++* Be aware that there is currently no error management in the ++ setspeed() code in the CPUFreq core. So only call yourself a ++ cpufreq_driver if you are really a working cpufreq_driver! ++ ++ ++ ++4. Mailing list and Links ++************************** ++ ++ ++Mailing List ++------------ ++There is a CPU frequency changing CVS commit and general list where ++you can report bugs, problems or submit patches. To post a message, ++send an email to cpufreq@www.linux.org.uk, to subscribe go to ++http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the ++mailing list are available to subscribers at ++http://www.linux.org.uk/mailman/private/cpufreq/. ++ ++ ++Links ++----- ++the FTP archives: ++* ftp://ftp.linux.org.uk/pub/linux/cpufreq/ ++ ++how to access the CVS repository: ++* http://www.arm.linux.org.uk/cvs/ ++ ++the CPUFreq Mailing list: ++* http://www.linux.org.uk/mailman/listinfo/cpufreq ++ ++Clock and voltage scaling for the SA-1100: ++* http://www.lart.tudelft.nl/projects/scaling ++ ++CPUFreq project homepage ++* http://www.brodo.de/cpufreq/ +diff -urN linux-2.4.26/Documentation/l3/structure linux-2.4.26-vrs1/Documentation/l3/structure +--- linux-2.4.26/Documentation/l3/structure 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/Documentation/l3/structure 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,36 @@ ++L3 Bus Driver ++------------- ++ ++The structure of the driver is as follows: ++ ++ +----------+ +----------+ +----------+ ++ | client 1 | | client 2 | | client 3 | ++ +-----^----+ +----^-----+ +----^-----+ ++ | | | ++ +-----v--------------v---------------v-----+ ++ | | ++ +-----^-------+ +-------^-----+ ++ | | core | | ++ +-----v----+ | | +----v-----+ ++ | device | | | | device | ++ | driver 1 | | | | driver 2 | ++ +-----^----+ | | +----^-----+ ++ | | services | | ++ +-----v-------+ +-------v-----+ ++ | | ++ +-----------------^----^-------------------+ ++ | | ++ | +-v---------+ ++ | | algorithm | ++ | | driver | ++ | +-v---------+ ++ | | ++ +-v----v-+ ++ | bus | ++ | driver | ++ +--------+ ++ ++Clients talk to the core to attach device drivers and bus adapters, and ++to instruct device drivers to perform actions. Device drivers then talk ++to the core to perform L3 bus transactions via the algorithm driver and ++ultimately bus driver. +diff -urN linux-2.4.26/Documentation/serial/driver linux-2.4.26-vrs1/Documentation/serial/driver +--- linux-2.4.26/Documentation/serial/driver 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/Documentation/serial/driver 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,208 @@ ++ ++ Low Level Serial API ++ -------------------- ++ ++ ++ $Id: driver,v 1.3 2001/11/24 23:24:47 rmk Exp $ ++ ++ ++This document is meant as a brief overview of some aspects of the new serial ++driver. It is not complete, any questions you have should be directed to ++ ++ ++The reference implementation is contained within serial_amba.c. ++ ++ ++ ++Low Level Serial Hardware Driver ++-------------------------------- ++ ++The low level serial hardware driver is responsible for supplying port ++information (defined by uart_port) and a set of control methods (defined ++by uart_ops) to the core serial driver. The low level driver is also ++responsible for handling interrupts for the port, and providing any ++console support. ++ ++ ++Console Support ++--------------- ++ ++The serial core provides a few helper functions. This includes identifing ++the correct port structure (via uart_get_console) and decoding command line ++arguments (uart_parse_options). ++ ++ ++Locking ++------- ++ ++Generally, all locking is done by the core driver, except for the interrupt ++functions. It is the responsibility of the low level hardware driver to ++perform the necessary locking there using info->lock. (since it is running ++in an interrupt, you only need to use spin_lock() and spin_unlock() from ++the interrupt handler). ++ ++ ++uart_ops ++-------- ++ ++The uart_ops structure is the main interface between serial_core and the ++hardware specific driver. It contains all the methods to control the ++hardware. ++ ++ tx_empty(port) ++ This function tests whether the transmitter fifo and shifter ++ for the port described by 'port' is empty. If it is empty, ++ this function should return TIOCSER_TEMT, otherwise return 0. ++ If the port does not support this operation, then it should ++ return TIOCSER_TEMT. ++ ++ set_mctrl(port, mctrl) ++ This function sets the modem control lines for port described ++ by 'port' to the state described by mctrl. The relevant bits ++ of mctrl are: ++ - TIOCM_RTS RTS signal. ++ - TIOCM_DTR DTR signal. ++ - TIOCM_OUT1 OUT1 signal. ++ - TIOCM_OUT2 OUT2 signal. ++ If the appropriate bit is set, the signal should be driven ++ active. If the bit is clear, the signal should be driven ++ inactive. ++ ++ get_mctrl(port) ++ Returns the current state of modem control inputs. The state ++ of the outputs should not be returned, since the core keeps ++ track of their state. The state information should include: ++ - TIOCM_DCD state of DCD signal ++ - TIOCM_CTS state of CTS signal ++ - TIOCM_DSR state of DSR signal ++ - TIOCM_RI state of RI signal ++ The bit is set if the signal is currently driven active. If ++ the port does not support CTS, DCD or DSR, the driver should ++ indicate that the signal is permanently active. If RI is ++ not available, the signal should not be indicated as active. ++ ++ stop_tx(port,from_tty) ++ Stop transmitting characters. This might be due to the CTS ++ line becoming inactive or the tty layer indicating we want ++ to stop transmission. ++ ++ start_tx(port,nonempty,from_tty) ++ start transmitting characters. (incidentally, nonempty will ++ always be nonzero, and shouldn't be used - it will be dropped). ++ ++ stop_rx(port) ++ Stop receiving characters; the port is in the process of ++ being closed. ++ ++ enable_ms(port) ++ Enable the modem status interrupts. ++ ++ break_ctl(port,ctl) ++ Control the transmission of a break signal. If ctl is ++ nonzero, the break signal should be transmitted. The signal ++ should be terminated when another call is made with a zero ++ ctl. ++ ++ startup(port,info) ++ Grab any interrupt resources and initialise any low level driver ++ state. Enable the port for reception. It should not activate ++ RTS nor DTR; this will be done via a separate call to set_mctrl. ++ ++ shutdown(port,info) ++ Disable the port, disable any break condition that may be in ++ effect, and free any interrupt resources. It should not disable ++ RTS nor DTR; this will have already been done via a separate ++ call to set_mctrl. ++ ++ change_speed(port,cflag,iflag,quot) ++ Change the port parameters, including word length, parity, stop ++ bits. Update read_status_mask and ignore_status_mask to indicate ++ the types of events we are interested in receiving. Relevant ++ cflag bits are: ++ CSIZE - word size ++ CSTOPB - 2 stop bits ++ PARENB - parity enable ++ PARODD - odd parity (when PARENB is in force) ++ CREAD - enable reception of characters (if not set, ++ still receive characters from the port, but ++ throw them away. ++ CRTSCTS - if set, enable CTS status change reporting ++ CLOCAL - if not set, enable modem status change ++ reporting. ++ Relevant iflag bits are: ++ INPCK - enable frame and parity error events to be ++ passed to the TTY layer. ++ BRKINT ++ PARMRK - both of these enable break events to be ++ passed to the TTY layer. ++ ++ IGNPAR - ignore parity and framing errors ++ IGNBRK - ignore break errors, If IGNPAR is also ++ set, ignore overrun errors as well. ++ The interaction of the iflag bits is as follows (parity error ++ given as an example): ++ Parity error INPCK IGNPAR ++ None n/a n/a character received ++ Yes n/a 0 character discarded ++ Yes 0 1 character received, marked as ++ TTY_NORMAL ++ Yes 1 1 character received, marked as ++ TTY_PARITY ++ ++ pm(port,state,oldstate) ++ perform any power management related activities on the specified ++ port. state indicates the new state (defined by ACPI D0-D3), ++ oldstate indicates the previous state. Essentially, D0 means ++ fully on, D3 means powered down. ++ ++ This function should not be used to grab any resources. ++ ++ type(port) ++ Return a pointer to a string constant describing the specified ++ port, or return NULL, in which case the string 'unknown' is ++ substituted. ++ ++ release_port(port) ++ Release any memory and IO region resources currently in use by ++ the port. ++ ++ request_port(port) ++ Request any memory and IO region resources required by the port. ++ If any fail, no resources should be registered when this function ++ returns, and it should return -EBUSY on failure. ++ ++ config_port(port,type) ++ Perform any autoconfiguration steps required for the port. `type` ++ contains a bit mask of the required configuration. UART_CONFIG_TYPE ++ indicates that the port requires detection and identification. ++ port->type should be set to the type found, or PORT_UNKNOWN if ++ no port was detected. ++ ++ UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal, ++ which should be probed using standard kernel autoprobing techniques. ++ This is not necessary on platforms where ports have interrupts ++ internally hard wired (eg, system on a chip implementations). ++ ++ verify_port(port,serinfo) ++ Verify the new serial port information contained within serinfo is ++ suitable for this port type. ++ ++ ioctl(port,cmd,arg) ++ Perform any port specific IOCTLs. IOCTL commands must be defined ++ using the standard numbering system found in ++ ++ ++Other notes ++----------- ++ ++It is intended some day to drop the 'unused' entries from uart_port, and ++allow low level drivers to register their own individual uart_port's with ++the core. This will allow drivers to use uart_port as a pointer to a ++structure containing both the uart_port entry with their own extensions, ++thus: ++ ++ struct my_port { ++ struct uart_port port; ++ int my_stuff; ++ }; ++ +diff -urN linux-2.4.26/MAINTAINERS linux-2.4.26-vrs1/MAINTAINERS +--- linux-2.4.26/MAINTAINERS 2004-04-19 11:44:14.000000000 +0100 ++++ linux-2.4.26-vrs1/MAINTAINERS 2004-04-18 21:47:49.000000000 +0100 +@@ -256,6 +256,8 @@ + ARM/STRONGARM110 PORT + P: Russell King + M: rmk@arm.linux.org.uk ++P: Vincent Sanders ++M: vince@arm.linux.org.uk + L: linux-arm-kernel@lists.arm.linux.org.uk + W: http://www.arm.linux.org.uk/ + S: Maintained +diff -urN linux-2.4.26/Makefile linux-2.4.26-vrs1/Makefile +--- linux-2.4.26/Makefile 2004-04-19 11:44:14.000000000 +0100 ++++ linux-2.4.26-vrs1/Makefile 2004-04-18 21:50:41.000000000 +0100 +@@ -1,7 +1,7 @@ + VERSION = 2 + PATCHLEVEL = 4 + SUBLEVEL = 26 +-EXTRAVERSION = ++EXTRAVERSION =-vrs1 + + KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) + +@@ -137,7 +137,10 @@ + + DRIVERS-$(CONFIG_ACPI_BOOT) += drivers/acpi/acpi.o + DRIVERS-$(CONFIG_PARPORT) += drivers/parport/driver.o +-DRIVERS-y += drivers/char/char.o \ ++DRIVERS-$(CONFIG_I2C) += drivers/i2c/i2c.o ++DRIVERS-$(CONFIG_L3) += drivers/l3/l3.o ++DRIVERS-y += drivers/serial/serial.o \ ++ drivers/char/char.o \ + drivers/block/block.o \ + drivers/misc/misc.o \ + drivers/net/net.o +@@ -161,6 +164,7 @@ + DRIVERS-y += drivers/cdrom/driver.o + endif + ++DRIVERS-$(CONFIG_SSI) += drivers/ssi/ssi.o + DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o + DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o + DRIVERS-$(CONFIG_MTD) += drivers/mtd/mtdlink.o +@@ -186,7 +190,6 @@ + DRIVERS-$(CONFIG_HIL) += drivers/hil/hil.o + DRIVERS-$(CONFIG_I2O) += drivers/message/i2o/i2o.o + DRIVERS-$(CONFIG_IRDA) += drivers/net/irda/irda.o +-DRIVERS-$(CONFIG_I2C) += drivers/i2c/i2c.o + DRIVERS-$(CONFIG_PHONE) += drivers/telephony/telephony.o + DRIVERS-$(CONFIG_MD) += drivers/md/mddev.o + DRIVERS-$(CONFIG_GSC) += drivers/gsc/gscbus.o +@@ -194,6 +197,8 @@ + DRIVERS-$(CONFIG_HOTPLUG_PCI) += drivers/hotplug/vmlinux-obj.o + DRIVERS-$(CONFIG_ISDN_BOOL) += drivers/isdn/vmlinux-obj.o + DRIVERS-$(CONFIG_CRYPTO) += crypto/crypto.o ++DRIVERS-$(CONFIG_PLD) += drivers/pld/pld.o ++DRIVERS-$(CONFIG_ARCH_AT91RM9200) += drivers/at91/at91drv.o + + DRIVERS := $(DRIVERS-y) + +@@ -274,11 +279,6 @@ + + export NETWORKS DRIVERS LIBS HEAD LDFLAGS LINKFLAGS MAKEBOOT ASFLAGS + +-.S.s: +- $(CPP) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -o $*.s $< +-.S.o: +- $(CC) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -c -o $*.o $< +- + Version: dummy + @rm -f include/linux/compile.h + +diff -urN linux-2.4.26/Rules.make linux-2.4.26-vrs1/Rules.make +--- linux-2.4.26/Rules.make 2004-02-27 20:03:23.000000000 +0000 ++++ linux-2.4.26-vrs1/Rules.make 2004-02-23 13:36:21.000000000 +0000 +@@ -51,15 +51,15 @@ + # + + %.s: %.c +- $(CC) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -S $< -o $@ ++ $(CC) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$(*F)) $(CFLAGS_$@) -S $< -o $@ + + %.i: %.c +- $(CPP) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) $< > $@ ++ $(CPP) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$(*F)) $(CFLAGS_$@) $< > $@ + + %.o: %.c +- $(CC) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -c -o $@ $< ++ $(CC) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$(*F)) $(CFLAGS_$@) -c -o $@ $< + @ ( \ +- echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS_nostdinc) $(CFLAGS_$@))),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS_nostdinc) $$(CFLAGS_$@))))' ; \ ++ echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS_nostdinc) $(CFLAGS_$(*F)) $(CFLAGS_$@))),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS_nostdinc) $$(CFLAGS_$(*F)) $$(CFLAGS_$@))))' ; \ + echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \ + echo 'endif' \ + ) > $(dir $@)/.$(notdir $@).flags +@@ -272,7 +272,8 @@ + endif # CONFIG_MODVERSIONS + + ifneq "$(strip $(export-objs))" "" +-$(export-objs): $(export-objs:.o=.c) $(TOPDIR)/include/linux/modversions.h ++$(export-objs): $(TOPDIR)/include/linux/modversions.h ++$(export-objs): %.o: %.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c) + @ ( \ + echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS_nostdinc) $(CFLAGS_$@) -DEXPORT_SYMTAB)),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS_nostdinc) $$(CFLAGS_$@) -DEXPORT_SYMTAB)))' ; \ +diff -urN linux-2.4.26/arch/alpha/config.in linux-2.4.26-vrs1/arch/alpha/config.in +--- linux-2.4.26/arch/alpha/config.in 2004-02-27 20:03:23.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/alpha/config.in 2004-02-23 13:36:21.000000000 +0000 +@@ -7,6 +7,7 @@ + define_bool CONFIG_UID16 n + define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n + define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y ++define_bool CONFIG_GENERIC_ISA_DMA y + + mainmenu_name "Kernel configuration of Linux for Alpha machines" + +diff -urN linux-2.4.26/arch/arm/Makefile linux-2.4.26-vrs1/arch/arm/Makefile +--- linux-2.4.26/arch/arm/Makefile 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/Makefile 2004-01-14 21:32:23.000000000 +0000 +@@ -52,7 +52,7 @@ + + CFLAGS_BOOT :=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float -Uarm + CFLAGS +=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float -Uarm +-AFLAGS +=$(apcs-y) $(arch-y) -mno-fpu -msoft-float ++AFLAGS +=$(apcs-y) $(arch-y) -msoft-float + + ifeq ($(CONFIG_CPU_26),y) + PROCESSOR := armo +diff -urN linux-2.4.26/arch/arm/boot/compressed/head.S linux-2.4.26-vrs1/arch/arm/boot/compressed/head.S +--- linux-2.4.26/arch/arm/boot/compressed/head.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/boot/compressed/head.S 2004-03-04 20:31:57.000000000 +0000 +@@ -40,6 +40,14 @@ + .macro writeb, rb + strb \rb, [r3, #0x3f8 << 2] + .endm ++#elif defined(CONFIG_ARCH_RISCSTATION) ++ .macro loadsp, rb ++ mov \rb, #0x03000000 ++ orr \rb, \rb, #0x00010000 ++ .endm ++ .macro writeb, rb ++ strb \rb, [r3, #0x3f8 << 2] ++ .endm + #elif defined(CONFIG_ARCH_INTEGRATOR) + .macro loadsp, rb + mov \rb, #0x16000000 +@@ -396,6 +404,20 @@ + mcr p15, 0, r0, c1, c0, 0 @ load control register + mov pc, r12 + ++__arm7_cache_on: ++ mov r12, lr ++ bl __setup_mmu ++ mov r0, #0 ++ mcr p15, 0, r0, c7, c0, 0 @ invalidate whole cache v3 ++ mcr p15, 0, r0, c5, c0, 0 @ invalidate whole TLB v3 ++ mcr p15, 0, r3, c2, c0, 0 @ load page table pointer ++ mov r0, #-1 ++ mcr p15, 0, r0, c3, c0, 0 @ load domain access control ++ mov r0, #0x7d ++ mcr p15, 0, r0, c1, c0, 0 @ load control register ++ mov pc, r12 ++ ++ + /* + * All code following this line is relocatable. It is relocated by + * the above code to the end of the decompressed kernel image and +@@ -480,9 +502,9 @@ + + .word 0x41007000 @ ARM7/710 + .word 0xfff8fe00 ++ b __arm7_cache_on + b __arm7_cache_off +- b __arm7_cache_off +- mov pc, lr ++ b __armv3_cache_flush + + .word 0x41807200 @ ARM720T (writethrough) + .word 0xffffff00 +@@ -490,14 +512,14 @@ + b __armv4_cache_off + mov pc, lr + +- .word 0x41129200 @ ARM920T +- .word 0xff00fff0 ++ .word 0x41009200 @ ARM920T, ARM922T, ARM926TEJ-S ++ .word 0xff00ff90 + b __armv4_cache_on + b __armv4_cache_off + b __armv4_cache_flush + +- .word 0x41029220 @ ARM922T +- .word 0xff00fff0 ++ .word 0x4100a200 @ ARM1020T/E, ARM1022E, ARM1026TEJ-S ++ .word 0xff00ff90 + b __armv4_cache_on + b __armv4_cache_off + b __armv4_cache_flush +diff -urN linux-2.4.26/arch/arm/config.in linux-2.4.26-vrs1/arch/arm/config.in +--- linux-2.4.26/arch/arm/config.in 2004-02-27 20:03:23.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/arm/config.in 2004-04-10 12:21:08.000000000 +0100 +@@ -144,6 +144,7 @@ + mainmenu_option next_comment + comment 'AT91RM9200 Implementations' + dep_bool ' Atmel AT91RM9200 Development Board' CONFIG_ARCH_AT91RM9200DK $CONFIG_ARCH_AT91RM9200 ++dep_bool ' Cogent CSB337' CONFIG_MACH_CSB337 $CONFIG_ARCH_AT91RM9200 + endmenu + + mainmenu_option next_comment +@@ -189,6 +190,12 @@ + define_bool CONFIG_ARCH_ACORN n + fi + ++if [ "$CONFIG_ARCH_CAMELOT" = "y" ]; then ++ define_bool CONFIG_PLD y ++else ++ define_bool CONFIG_PLD n ++fi ++ + ##################################################################### + # Footbridge support + if [ "$CONFIG_ARCH_CO285" = "y" -o \ +@@ -315,26 +322,42 @@ + # ARM922T + if [ "$CONFIG_ARCH_CAMELOT" = "y" ]; then + define_bool CONFIG_CPU_ARM922T y +- define_bool CONFIG_PLD y + else +- define_bool CONFIG_CPU_ARM922T n +- define_bool CONFIG_PLD n ++ if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then ++ bool 'Support ARM922T(Excalibur) processor' CONFIG_ARM922T ++ else ++ define_bool CONFIG_CPU_ARM922T n ++ fi + fi + + # ARM926T + if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then +- bool 'Support ARM926T processor' CONFIG_CPU_ARM926T ++ bool 'Support ARM926TEJ-S processor' CONFIG_CPU_ARM926T + else + define_bool CONFIG_CPU_ARM926T n + fi + + # ARM1020 + if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then +- bool 'Support ARM1020 processor' CONFIG_CPU_ARM1020 ++ bool 'Support ARM1020T (Rev0) processor' CONFIG_CPU_ARM1020 + else + define_bool CONFIG_CPU_ARM1020 n + fi + ++# ARM1020E ++if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then ++ bool 'Support ARM1020E (Rev1) processor' CONFIG_CPU_ARM1020E ++else ++ define_bool CONFIG_CPU_ARM1020E n ++fi ++ ++# ARM1022 ++if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then ++ bool 'Support ARM1022 processor' CONFIG_CPU_ARM1020E ++else ++ define_bool CONFIG_CPU_ARM1022 n ++fi ++ + # ARM1026EJ-S + if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then + bool 'Support ARM1026EJ-S processor' CONFIG_CPU_ARM1026 +@@ -388,25 +411,29 @@ + + if [ "$CONFIG_CPU_ARM720T" = "y" -o "$CONFIG_CPU_ARM920T" = "y" -o \ + "$CONFIG_CPU_ARM922T" = "y" -o "$CONFIG_CPU_ARM926T" = "y" -o \ +- "$CONFIG_CPU_ARM1020" = "y" -o "$CONFIG_CPU_ARM1026" = "y" ]; then ++ "$CONFIG_CPU_ARM1020" = "y" -o "$CONFIG_CPU_ARM1020E" = "y" -o \ ++ "$CONFIG_CPU_ARM1022" = "y" -o "$CONFIG_CPU_ARM1026" = "y" ]; then + dep_bool 'Support Thumb instructions (EXPERIMENTAL)' CONFIG_ARM_THUMB $CONFIG_EXPERIMENTAL + fi + if [ "$CONFIG_CPU_ARM920T" = "y" -o "$CONFIG_CPU_ARM922T" = "y" -o \ + "$CONFIG_CPU_ARM926T" = "y" -o "$CONFIG_CPU_ARM1020" = "y" -o \ ++ "$CONFIG_CPU_ARM1020E" = "y" -o "$CONFIG_CPU_ARM1022" = "y" -o \ + "$CONFIG_CPU_ARM1026" = "y" ]; then + bool 'Disable I-Cache' CONFIG_CPU_ICACHE_DISABLE + bool 'Disable D-Cache' CONFIG_CPU_DCACHE_DISABLE +- if [ "$CONFIG_CPU_DISABLE_DCACHE" = "n" ]; then ++ if [ "$CONFIG_CPU_DCACHE_DISABLE" = "n" ]; then + bool 'Force write through D-cache' CONFIG_CPU_DCACHE_WRITETHROUGH + fi + fi + if [ "$CONFIG_CPU_ARM926T" = "y" -o "$CONFIG_CPU_ARM1020" = "y" -o \ ++ "$CONFIG_CPU_ARM1020E" = "y" -o "$CONFIG_CPU_ARM1022" = "y" -o \ + "$CONFIG_CPU_ARM1026" = "y" ]; then + if [ "$CONFIG_CPU_ICACHE_DISABLE" = "n" -o "$CONFIG_CPU_DCACHE_DISABLE" = "n" ]; then + bool 'Round robin I and D cache replacement algorithm' CONFIG_CPU_CACHE_ROUND_ROBIN + fi + fi +-if [ "$CONFIG_CPU_ARM1020" = "y" -o "$CONFIG_CPU_ARM1026" = "y" ]; then ++if [ "$CONFIG_CPU_ARM1020" = "y" -o "$CONFIG_CPU_ARM1020E" = "y" -o \ ++ "$CONFIG_CPU_ARM1026" = "y" -o "$CONFIG_CPU_ARM1022" = "y" ]; then + bool 'Disable branch prediction' CONFIG_CPU_BPREDICT_DISABLE + fi + +@@ -514,7 +541,8 @@ + "$CONFIG_ARCH_INTEGRATOR" = "y" -o \ + "$CONFIG_ARCH_CDB89712" = "y" -o \ + "$CONFIG_ARCH_P720T" = "y" -o \ +- "$CONFIG_ARCH_OMAHA" = "y" ]; then ++ "$CONFIG_ARCH_OMAHA" = "y" -o \ ++ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then + bool 'Timer and CPU usage LEDs' CONFIG_LEDS + if [ "$CONFIG_LEDS" = "y" ]; then + if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \ +@@ -524,7 +552,8 @@ + "$CONFIG_ARCH_SA1100" = "y" -o \ + "$CONFIG_ARCH_INTEGRATOR" = "y" -o \ + "$CONFIG_ARCH_P720T" = "y" -o \ +- "$CONFIG_ARCH_OMAHA" = "y" ]; then ++ "$CONFIG_ARCH_OMAHA" = "y" -o \ ++ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then + bool ' Timer LED' CONFIG_LEDS_TIMER + bool ' CPU usage LED' CONFIG_LEDS_CPU + fi +@@ -729,10 +758,7 @@ + dep_bool ' Kernel low-level debugging functions' CONFIG_DEBUG_LL $CONFIG_DEBUG_KERNEL + dep_bool ' Kernel low-level debugging messages via footbridge serial port' CONFIG_DEBUG_DC21285_PORT $CONFIG_DEBUG_LL $CONFIG_FOOTBRIDGE + dep_bool ' Kernel low-level debugging messages via UART2' CONFIG_DEBUG_CLPS711X_UART2 $CONFIG_DEBUG_LL $CONFIG_ARCH_CLPS711X +- +-int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0 +- + endmenu + +-source crypto/Config.in + source lib/Config.in ++ +diff -urN linux-2.4.26/arch/arm/def-configs/at91rm9200dk linux-2.4.26-vrs1/arch/arm/def-configs/at91rm9200dk +--- linux-2.4.26/arch/arm/def-configs/at91rm9200dk 2004-02-27 20:03:23.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/arm/def-configs/at91rm9200dk 2004-03-04 22:47:46.000000000 +0000 +@@ -111,6 +111,7 @@ + # AT91RM9200 Implementations + # + CONFIG_ARCH_AT91RM9200DK=y ++# CONFIG_MACH_CSB337 is not set + + # + # CLPS711X/EP721X Implementations +@@ -125,6 +126,7 @@ + # CONFIG_ARCH_EP7211 is not set + # CONFIG_ARCH_EP7212 is not set + # CONFIG_ARCH_ACORN is not set ++# CONFIG_PLD is not set + # CONFIG_FOOTBRIDGE is not set + # CONFIG_FOOTBRIDGE_HOST is not set + # CONFIG_FOOTBRIDGE_ADDIN is not set +@@ -135,9 +137,10 @@ + # CONFIG_CPU_ARM720T is not set + CONFIG_CPU_ARM920T=y + # CONFIG_CPU_ARM922T is not set +-# CONFIG_PLD is not set + # CONFIG_CPU_ARM926T is not set + # CONFIG_CPU_ARM1020 is not set ++# CONFIG_CPU_ARM1020E is not set ++# CONFIG_CPU_ARM1022 is not set + # CONFIG_CPU_ARM1026 is not set + # CONFIG_CPU_SA110 is not set + # CONFIG_CPU_SA1100 is not set +@@ -146,6 +149,7 @@ + # CONFIG_ARM_THUMB is not set + # CONFIG_CPU_ICACHE_DISABLE is not set + # CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set + # CONFIG_DISCONTIGMEM is not set + + # +@@ -164,6 +168,7 @@ + # CONFIG_BSD_PROCESS_ACCT is not set + CONFIG_SYSCTL=y + CONFIG_FPE_NWFPE=y ++# CONFIG_FPE_NWFPE_XP is not set + # CONFIG_FPE_FASTFPE is not set + CONFIG_KCORE_ELF=y + # CONFIG_KCORE_AOUT is not set +@@ -173,6 +178,9 @@ + # CONFIG_PM is not set + # CONFIG_ARTHUR is not set + CONFIG_CMDLINE="mem=32M console=ttyS0,115200 initrd=0x20210000,3145728 root=/dev/ram rw" ++CONFIG_LEDS=y ++CONFIG_LEDS_TIMER=y ++# CONFIG_LEDS_CPU is not set + CONFIG_ALIGNMENT_TRAP=y + + # +@@ -204,6 +212,7 @@ + # CONFIG_MTD_CFI_ADV_OPTIONS is not set + # CONFIG_MTD_CFI_INTELEXT is not set + CONFIG_MTD_CFI_AMDSTD=y ++# CONFIG_MTD_CFI_STAA is not set + # CONFIG_MTD_RAM is not set + # CONFIG_MTD_ROM is not set + # CONFIG_MTD_ABSENT is not set +@@ -230,7 +239,9 @@ + # CONFIG_MTD_AUTCPU12 is not set + # CONFIG_MTD_EDB7312 is not set + # CONFIG_MTD_IMPA7 is not set ++# CONFIG_MTD_CEIVA is not set + # CONFIG_MTD_PCI is not set ++# CONFIG_MTD_PCMCIA is not set + + # + # Self-contained MTD device drivers +@@ -250,9 +261,9 @@ + # NAND Flash Device Drivers + # + CONFIG_MTD_NAND=y +-CONFIG_MTD_NAND_ECC=y + # CONFIG_MTD_NAND_VERIFY_WRITE is not set +-CONFIG_MTD_AT91_SMARTMEDIA=y ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_AT91_SMARTMEDIA is not set + + # + # Plug and Play configuration +@@ -269,6 +280,7 @@ + # CONFIG_BLK_CPQ_DA is not set + # CONFIG_BLK_CPQ_CISS_DA is not set + # CONFIG_CISS_SCSI_TAPE is not set ++# CONFIG_CISS_MONITOR_THREAD is not set + # CONFIG_BLK_DEV_DAC960 is not set + # CONFIG_BLK_DEV_UMEM is not set + # CONFIG_BLK_DEV_LOOP is not set +@@ -276,6 +288,7 @@ + CONFIG_BLK_DEV_RAM=y + CONFIG_BLK_DEV_RAM_SIZE=8192 + CONFIG_BLK_DEV_INITRD=y ++# CONFIG_BLK_STATS is not set + + # + # Multi-device support (RAID and LVM) +@@ -312,6 +325,12 @@ + # CONFIG_SYN_COOKIES is not set + # CONFIG_IPV6 is not set + # CONFIG_KHTTPD is not set ++ ++# ++# SCTP Configuration (EXPERIMENTAL) ++# ++CONFIG_IPV6_SCTP__=y ++# CONFIG_IP_SCTP is not set + # CONFIG_ATM is not set + # CONFIG_VLAN_8021Q is not set + # CONFIG_IPX is not set +@@ -382,10 +401,12 @@ + # + # CONFIG_ACENIC is not set + # CONFIG_DL2K is not set ++# CONFIG_E1000 is not set + # CONFIG_MYRI_SBUS is not set + # CONFIG_NS83820 is not set + # CONFIG_HAMACHI is not set + # CONFIG_YELLOWFIN is not set ++# CONFIG_R8169 is not set + # CONFIG_SK98LIN is not set + # CONFIG_TIGON3 is not set + # CONFIG_FDDI is not set +@@ -455,6 +476,8 @@ + # CONFIG_INPUT_MOUSEDEV is not set + # CONFIG_INPUT_JOYDEV is not set + # CONFIG_INPUT_EVDEV is not set ++# CONFIG_INPUT_UINPUT is not set ++# CONFIG_INPUT_MX1TS is not set + + # + # Character devices +@@ -502,6 +525,7 @@ + # + CONFIG_I2C=y + # CONFIG_I2C_ALGOBIT is not set ++# CONFIG_SCx200_ACB is not set + # CONFIG_I2C_ALGOPCF is not set + CONFIG_I2C_AT91=y + CONFIG_I2C_CHARDEV=y +@@ -528,6 +552,11 @@ + # + # CONFIG_INPUT_GAMEPORT is not set + # CONFIG_QIC02_TAPE is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_IPMI_PANIC_EVENT is not set ++# CONFIG_IPMI_DEVICE_INTERFACE is not set ++# CONFIG_IPMI_KCS is not set ++# CONFIG_IPMI_WATCHDOG is not set + + # + # Watchdog Cards +@@ -536,12 +565,14 @@ + CONFIG_WATCHDOG_NOWAYOUT=y + # CONFIG_ACQUIRE_WDT is not set + # CONFIG_ADVANTECH_WDT is not set ++# CONFIG_ALIM1535_WDT is not set + # CONFIG_ALIM7101_WDT is not set + # CONFIG_SC520_WDT is not set + # CONFIG_PCWATCHDOG is not set + # CONFIG_21285_WATCHDOG is not set + # CONFIG_977_WATCHDOG is not set + # CONFIG_SA1100_WATCHDOG is not set ++# CONFIG_EPXA_WATCHDOG is not set + # CONFIG_OMAHA_WATCHDOG is not set + CONFIG_AT91_WATCHDOG=y + # CONFIG_EUROTECH_WDT is not set +@@ -551,11 +582,16 @@ + # CONFIG_MIXCOMWD is not set + # CONFIG_60XX_WDT is not set + # CONFIG_SC1200_WDT is not set ++# CONFIG_SCx200_WDT is not set + # CONFIG_SOFT_WATCHDOG is not set + # CONFIG_W83877F_WDT is not set + # CONFIG_WDT is not set + # CONFIG_WDTPCI is not set + # CONFIG_MACHZ_WDT is not set ++# CONFIG_AMD7XX_TCO is not set ++# CONFIG_SCx200 is not set ++# CONFIG_SCx200_GPIO is not set ++# CONFIG_AMD_PM768 is not set + # CONFIG_NVRAM is not set + # CONFIG_RTC is not set + CONFIG_AT91_RTC=y +@@ -568,6 +604,10 @@ + # + # CONFIG_FTAPE is not set + # CONFIG_AGP is not set ++ ++# ++# Direct Rendering Manager (XFree86 DRI support) ++# + # CONFIG_DRM is not set + + # +@@ -579,6 +619,7 @@ + # File systems + # + # CONFIG_QUOTA is not set ++# CONFIG_QFMT_V2 is not set + # CONFIG_AUTOFS_FS is not set + # CONFIG_AUTOFS4_FS is not set + # CONFIG_REISERFS_FS is not set +@@ -588,6 +629,9 @@ + # CONFIG_ADFS_FS_RW is not set + # CONFIG_AFFS_FS is not set + # CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BEFS_DEBUG is not set + # CONFIG_BFS_FS is not set + # CONFIG_EXT3_FS is not set + # CONFIG_JBD is not set +@@ -605,6 +649,9 @@ + # CONFIG_ISO9660_FS is not set + # CONFIG_JOLIET is not set + # CONFIG_ZISOFS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_JFS_DEBUG is not set ++# CONFIG_JFS_STATISTICS is not set + # CONFIG_MINIX_FS is not set + # CONFIG_VXFS_FS is not set + # CONFIG_NTFS_FS is not set +@@ -624,6 +671,11 @@ + # CONFIG_UDF_RW is not set + # CONFIG_UFS_FS is not set + # CONFIG_UFS_FS_WRITE is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_XFS_QUOTA is not set ++# CONFIG_XFS_RT is not set ++# CONFIG_XFS_TRACE is not set ++# CONFIG_XFS_DEBUG is not set + + # + # Network File Systems +@@ -632,9 +684,11 @@ + # CONFIG_INTERMEZZO_FS is not set + # CONFIG_NFS_FS is not set + # CONFIG_NFS_V3 is not set ++# CONFIG_NFS_DIRECTIO is not set + # CONFIG_ROOT_NFS is not set + # CONFIG_NFSD is not set + # CONFIG_NFSD_V3 is not set ++# CONFIG_NFSD_TCP is not set + # CONFIG_SUNRPC is not set + # CONFIG_LOCKD is not set + # CONFIG_SMB_FS is not set +@@ -648,7 +702,6 @@ + # CONFIG_NCPFS_NLS is not set + # CONFIG_NCPFS_EXTRAS is not set + # CONFIG_ZISOFS_FS is not set +-# CONFIG_ZLIB_FS_INFLATE is not set + + # + # Partition Types +@@ -674,16 +727,18 @@ + # CONFIG_USB_DEBUG is not set + # CONFIG_USB_DEVICEFS is not set + # CONFIG_USB_BANDWIDTH is not set +-# CONFIG_USB_LONG_TIMEOUT is not set + # CONFIG_USB_EHCI_HCD is not set + # CONFIG_USB_UHCI is not set + # CONFIG_USB_UHCI_ALT is not set + # CONFIG_USB_OHCI is not set + # CONFIG_USB_OHCI_SA1111 is not set ++# CONFIG_USB_SL811HS_ALT is not set ++# CONFIG_USB_SL811HS is not set + CONFIG_USB_OHCI_AT91=y + # CONFIG_USB_AUDIO is not set + # CONFIG_USB_EMI26 is not set + # CONFIG_USB_BLUETOOTH is not set ++# CONFIG_USB_MIDI is not set + # CONFIG_USB_STORAGE is not set + # CONFIG_USB_STORAGE_DEBUG is not set + # CONFIG_USB_STORAGE_DATAFAB is not set +@@ -692,6 +747,7 @@ + # CONFIG_USB_STORAGE_DPCM is not set + # CONFIG_USB_STORAGE_HP8200e is not set + # CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set + # CONFIG_USB_STORAGE_JUMPSHOT is not set + # CONFIG_USB_ACM is not set + # CONFIG_USB_PRINTER is not set +@@ -700,7 +756,10 @@ + # CONFIG_USB_HIDDEV is not set + # CONFIG_USB_KBD is not set + # CONFIG_USB_MOUSE is not set ++# CONFIG_USB_AIPTEK is not set + # CONFIG_USB_WACOM is not set ++# CONFIG_USB_KBTAB is not set ++# CONFIG_USB_POWERMATE is not set + # CONFIG_USB_DC2XX is not set + # CONFIG_USB_MDC800 is not set + # CONFIG_USB_SCANNER is not set +@@ -718,35 +777,16 @@ + # USB Serial Converter support + # + # CONFIG_USB_SERIAL is not set +-# CONFIG_USB_SERIAL_GENERIC is not set +-# CONFIG_USB_SERIAL_BELKIN is not set +-# CONFIG_USB_SERIAL_WHITEHEAT is not set +-# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +-# CONFIG_USB_SERIAL_EMPEG is not set +-# CONFIG_USB_SERIAL_FTDI_SIO is not set +-# CONFIG_USB_SERIAL_VISOR is not set +-# CONFIG_USB_SERIAL_IPAQ is not set +-# CONFIG_USB_SERIAL_IR is not set +-# CONFIG_USB_SERIAL_EDGEPORT is not set +-# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +-# CONFIG_USB_SERIAL_KEYSPAN is not set +-# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +-# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +-# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +-# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +-# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +-# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +-# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +-# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +-# CONFIG_USB_SERIAL_MCT_U232 is not set +-# CONFIG_USB_SERIAL_KLSI is not set +-# CONFIG_USB_SERIAL_PL2303 is not set +-# CONFIG_USB_SERIAL_CYBERJACK is not set +-# CONFIG_USB_SERIAL_XIRCOM is not set +-# CONFIG_USB_SERIAL_OMNINET is not set + # CONFIG_USB_RIO500 is not set + # CONFIG_USB_AUERSWALD is not set ++# CONFIG_USB_TIGL is not set + # CONFIG_USB_BRLVGER is not set ++# CONFIG_USB_LCD is not set ++ ++# ++# Support for USB gadgets ++# ++# CONFIG_USB_GADGET is not set + + # + # Bluetooth support +@@ -770,3 +810,10 @@ + CONFIG_DEBUG_LL=y + # CONFIG_DEBUG_DC21285_PORT is not set + # CONFIG_DEBUG_CLPS711X_UART2 is not set ++ ++# ++# Library routines ++# ++CONFIG_CRC32=y ++# CONFIG_ZLIB_INFLATE is not set ++# CONFIG_ZLIB_DEFLATE is not set +diff -urN linux-2.4.26/arch/arm/def-configs/csb337 linux-2.4.26-vrs1/arch/arm/def-configs/csb337 +--- linux-2.4.26/arch/arm/def-configs/csb337 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/def-configs/csb337 2004-03-04 22:44:33.000000000 +0000 +@@ -0,0 +1,760 @@ ++# ++# Automatically generated by make menuconfig: don't edit ++# ++CONFIG_ARM=y ++# CONFIG_EISA is not set ++# CONFIG_SBUS is not set ++# CONFIG_MCA is not set ++CONFIG_UID16=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set ++# CONFIG_GENERIC_BUST_SPINLOCK is not set ++# CONFIG_GENERIC_ISA_DMA is not set ++ ++# ++# Code maturity level options ++# ++CONFIG_EXPERIMENTAL=y ++# CONFIG_OBSOLETE is not set ++ ++# ++# Loadable module support ++# ++CONFIG_MODULES=y ++# CONFIG_MODVERSIONS is not set ++CONFIG_KMOD=y ++ ++# ++# System Type ++# ++# CONFIG_ARCH_ANAKIN is not set ++# CONFIG_ARCH_ARCA5K is not set ++# CONFIG_ARCH_CLPS7500 is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_CO285 is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_CAMELOT is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_OMAHA is not set ++# CONFIG_ARCH_L7200 is not set ++# CONFIG_ARCH_MX1ADS is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_RISCSTATION is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_SHARK is not set ++CONFIG_ARCH_AT91RM9200=y ++ ++# ++# Archimedes/A5000 Implementations ++# ++# CONFIG_ARCH_ARC is not set ++# CONFIG_ARCH_A5K is not set ++ ++# ++# Footbridge Implementations ++# ++# CONFIG_ARCH_CATS is not set ++# CONFIG_ARCH_PERSONAL_SERVER is not set ++# CONFIG_ARCH_EBSA285_ADDIN is not set ++# CONFIG_ARCH_EBSA285_HOST is not set ++# CONFIG_ARCH_NETWINDER is not set ++ ++# ++# SA11x0 Implementations ++# ++# CONFIG_SA1100_ACCELENT is not set ++# CONFIG_SA1100_ASSABET is not set ++# CONFIG_ASSABET_NEPONSET is not set ++# CONFIG_SA1100_ADSAGC is not set ++# CONFIG_SA1100_ADSBITSY is not set ++# CONFIG_SA1100_ADSBITSYPLUS is not set ++# CONFIG_SA1100_BRUTUS is not set ++# CONFIG_SA1100_CEP is not set ++# CONFIG_SA1100_CERF is not set ++# CONFIG_SA1100_H3100 is not set ++# CONFIG_SA1100_H3600 is not set ++# CONFIG_SA1100_H3800 is not set ++# CONFIG_SA1100_H3XXX is not set ++# CONFIG_H3600_SLEEVE is not set ++# CONFIG_SA1100_EXTENEX1 is not set ++# CONFIG_SA1100_FLEXANET is not set ++# CONFIG_SA1100_FREEBIRD is not set ++# CONFIG_SA1100_FRODO is not set ++# CONFIG_SA1100_GRAPHICSCLIENT is not set ++# CONFIG_SA1100_GRAPHICSMASTER is not set ++# CONFIG_SA1100_HACKKIT is not set ++# CONFIG_SA1100_BADGE4 is not set ++# CONFIG_SA1100_JORNADA720 is not set ++# CONFIG_SA1100_HUW_WEBPANEL is not set ++# CONFIG_SA1100_ITSY is not set ++# CONFIG_SA1100_LART is not set ++# CONFIG_SA1100_NANOENGINE is not set ++# CONFIG_SA1100_OMNIMETER is not set ++# CONFIG_SA1100_PANGOLIN is not set ++# CONFIG_SA1100_PLEB is not set ++# CONFIG_SA1100_PT_SYSTEM3 is not set ++# CONFIG_SA1100_SHANNON is not set ++# CONFIG_SA1100_SHERMAN is not set ++# CONFIG_SA1100_SIMPAD is not set ++# CONFIG_SA1100_SIMPUTER is not set ++# CONFIG_SA1100_PFS168 is not set ++# CONFIG_SA1100_VICTOR is not set ++# CONFIG_SA1100_XP860 is not set ++# CONFIG_SA1100_YOPY is not set ++# CONFIG_SA1100_USB is not set ++# CONFIG_SA1100_USB_NETLINK is not set ++# CONFIG_SA1100_USB_CHAR is not set ++# CONFIG_SA1100_SSP is not set ++ ++# ++# AT91RM9200 Implementations ++# ++# CONFIG_ARCH_AT91RM9200DK is not set ++CONFIG_MACH_CSB337=y ++ ++# ++# CLPS711X/EP721X Implementations ++# ++# CONFIG_ARCH_AUTCPU12 is not set ++# CONFIG_ARCH_CDB89712 is not set ++# CONFIG_ARCH_CLEP7312 is not set ++# CONFIG_ARCH_EDB7211 is not set ++# CONFIG_ARCH_FORTUNET is not set ++# CONFIG_ARCH_GUIDEA07 is not set ++# CONFIG_ARCH_P720T is not set ++# CONFIG_ARCH_EP7211 is not set ++# CONFIG_ARCH_EP7212 is not set ++# CONFIG_ARCH_ACORN is not set ++# CONFIG_PLD is not set ++# CONFIG_FOOTBRIDGE is not set ++# CONFIG_FOOTBRIDGE_HOST is not set ++# CONFIG_FOOTBRIDGE_ADDIN is not set ++CONFIG_CPU_32=y ++# CONFIG_CPU_26 is not set ++# CONFIG_CPU_ARM610 is not set ++# CONFIG_CPU_ARM710 is not set ++# CONFIG_CPU_ARM720T is not set ++CONFIG_CPU_ARM920T=y ++# CONFIG_CPU_ARM922T is not set ++# CONFIG_CPU_ARM926T is not set ++# CONFIG_CPU_ARM1020 is not set ++# CONFIG_CPU_ARM1020E is not set ++# CONFIG_CPU_ARM1022 is not set ++# CONFIG_CPU_ARM1026 is not set ++# CONFIG_CPU_SA110 is not set ++# CONFIG_CPU_SA1100 is not set ++# CONFIG_CPU_32v3 is not set ++CONFIG_CPU_32v4=y ++# CONFIG_ARM_THUMB is not set ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set ++# CONFIG_DISCONTIGMEM is not set ++ ++# ++# General setup ++# ++# CONFIG_PCI is not set ++# CONFIG_ISA is not set ++# CONFIG_ISA_DMA is not set ++# CONFIG_ZBOOT_ROM is not set ++CONFIG_ZBOOT_ROM_TEXT=0 ++CONFIG_ZBOOT_ROM_BSS=0 ++# CONFIG_HOTPLUG is not set ++# CONFIG_PCMCIA is not set ++CONFIG_NET=y ++CONFIG_SYSVIPC=y ++# CONFIG_BSD_PROCESS_ACCT is not set ++CONFIG_SYSCTL=y ++CONFIG_FPE_NWFPE=y ++# CONFIG_FPE_NWFPE_XP is not set ++# CONFIG_FPE_FASTFPE is not set ++CONFIG_KCORE_ELF=y ++# CONFIG_KCORE_AOUT is not set ++# CONFIG_BINFMT_AOUT is not set ++CONFIG_BINFMT_ELF=y ++# CONFIG_BINFMT_MISC is not set ++# CONFIG_PM is not set ++# CONFIG_ARTHUR is not set ++CONFIG_CMDLINE="mem=32M console=ttyS0,38400 initrd=0x20210000,3145728 root=/dev/ram rw" ++# CONFIG_LEDS is not set ++CONFIG_ALIGNMENT_TRAP=y ++ ++# ++# Parallel port support ++# ++# CONFIG_PARPORT is not set ++ ++# ++# Memory Technology Devices (MTD) ++# ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++# CONFIG_MTD_PARTITIONS is not set ++# CONFIG_MTD_CONCAT is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++# CONFIG_MTD_AFS_PARTS is not set ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++CONFIG_MTD_CFI=y ++CONFIG_MTD_JEDECPROBE=y ++CONFIG_MTD_GEN_PROBE=y ++# CONFIG_MTD_CFI_ADV_OPTIONS is not set ++CONFIG_MTD_CFI_INTELEXT=y ++# CONFIG_MTD_CFI_AMDSTD is not set ++# CONFIG_MTD_CFI_STAA is not set ++# CONFIG_MTD_RAM is not set ++CONFIG_MTD_ROM=y ++# CONFIG_MTD_ABSENT is not set ++# CONFIG_MTD_OBSOLETE_CHIPS is not set ++# CONFIG_MTD_AMDSTD is not set ++# CONFIG_MTD_SHARP is not set ++# CONFIG_MTD_JEDEC is not set ++ ++# ++# Mapping drivers for chip access ++# ++CONFIG_MTD_PHYSMAP=y ++CONFIG_MTD_PHYSMAP_START=10000000 ++CONFIG_MTD_PHYSMAP_LEN=200000 ++CONFIG_MTD_PHYSMAP_BUSWIDTH=2 ++# CONFIG_MTD_NORA is not set ++# CONFIG_MTD_ARM_INTEGRATOR is not set ++# CONFIG_MTD_CDB89712 is not set ++# CONFIG_MTD_SA1100 is not set ++# CONFIG_MTD_DC21285 is not set ++# CONFIG_MTD_IQ80310 is not set ++# CONFIG_MTD_FORTUNET is not set ++# CONFIG_MTD_EPXA is not set ++# CONFIG_MTD_AUTCPU12 is not set ++# CONFIG_MTD_EDB7312 is not set ++# CONFIG_MTD_IMPA7 is not set ++# CONFIG_MTD_CEIVA is not set ++# CONFIG_MTD_PCI is not set ++# CONFIG_MTD_PCMCIA is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_PMC551 is not set ++# CONFIG_MTD_SLRAM is not set ++CONFIG_MTD_AT91_DATAFLASH=y ++# CONFIG_MTD_AT91_DATAFLASH_CARD is not set ++# CONFIG_MTD_MTDRAM is not set ++# CONFIG_MTD_BLKMTD is not set ++# CONFIG_MTD_DOC1000 is not set ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOCPROBE is not set ++ ++# ++# NAND Flash Device Drivers ++# ++CONFIG_MTD_NAND=y ++# CONFIG_MTD_NAND_VERIFY_WRITE is not set ++CONFIG_MTD_NAND_IDS=y ++# CONFIG_MTD_AT91_SMARTMEDIA 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_CISS_SCSI_TAPE is not set ++# CONFIG_CISS_MONITOR_THREAD is not set ++# CONFIG_BLK_DEV_DAC960 is not set ++# CONFIG_BLK_DEV_UMEM is not set ++# CONFIG_BLK_DEV_LOOP is not set ++# CONFIG_BLK_DEV_NBD is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_SIZE=8192 ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_BLK_STATS is not set ++ ++# ++# Multi-device support (RAID and LVM) ++# ++# CONFIG_MD is not set ++# CONFIG_BLK_DEV_MD is not set ++# CONFIG_MD_LINEAR is not set ++# CONFIG_MD_RAID0 is not set ++# CONFIG_MD_RAID1 is not set ++# CONFIG_MD_RAID5 is not set ++# CONFIG_MD_MULTIPATH is not set ++# CONFIG_BLK_DEV_LVM is not set ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++# CONFIG_PACKET_MMAP is not set ++# CONFIG_NETLINK_DEV 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=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_BOOTP=y ++# CONFIG_IP_PNP_RARP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE is not set ++# CONFIG_ARPD 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 ++ ++# ++# SCTP Configuration (EXPERIMENTAL) ++# ++CONFIG_IPV6_SCTP__=y ++# CONFIG_IP_SCTP is not set ++# CONFIG_ATM is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++ ++# ++# Appletalk devices ++# ++# CONFIG_DEV_APPLETALK 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 ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN 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_ETHERTAP is not set ++ ++# ++# Ethernet (10 or 100Mbit) ++# ++CONFIG_NET_ETHERNET=y ++# CONFIG_ARM_AM79C961A is not set ++# CONFIG_ARM_CIRRUS is not set ++CONFIG_AT91_ETHER=y ++# CONFIG_AT91_ETHER_RMII is not set ++# CONFIG_SUNLANCE is not set ++# CONFIG_SUNBMAC is not set ++# CONFIG_SUNQE is not set ++# CONFIG_SUNGEM is not set ++# 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_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_DL2K is not set ++# CONFIG_E1000 is not set ++# CONFIG_MYRI_SBUS is not set ++# CONFIG_NS83820 is not set ++# CONFIG_HAMACHI is not set ++# CONFIG_YELLOWFIN is not set ++# CONFIG_R8169 is not set ++# CONFIG_SK98LIN is not set ++# CONFIG_TIGON3 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 ++ ++# ++# ATA/ATAPI/MFM/RLL support ++# ++# CONFIG_IDE is not set ++# CONFIG_BLK_DEV_HD 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 ++ ++# ++# ISDN subsystem ++# ++# CONFIG_ISDN is not set ++ ++# ++# Input core support ++# ++# CONFIG_INPUT is not set ++# CONFIG_INPUT_KEYBDEV is not set ++# CONFIG_INPUT_MOUSEDEV is not set ++# CONFIG_INPUT_JOYDEV is not set ++# CONFIG_INPUT_EVDEV is not set ++# CONFIG_INPUT_UINPUT is not set ++# CONFIG_INPUT_MX1TS 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_AT91_SPIDEV=y ++ ++# ++# Serial drivers ++# ++# CONFIG_SERIAL_ANAKIN is not set ++# CONFIG_SERIAL_ANAKIN_CONSOLE is not set ++# CONFIG_SERIAL_AMBA is not set ++# CONFIG_SERIAL_AMBA_CONSOLE is not set ++# CONFIG_SERIAL_CLPS711X is not set ++# CONFIG_SERIAL_CLPS711X_CONSOLE is not set ++# CONFIG_SERIAL_21285 is not set ++# CONFIG_SERIAL_21285_OLD is not set ++# CONFIG_SERIAL_21285_CONSOLE is not set ++# CONFIG_SERIAL_UART00 is not set ++# CONFIG_SERIAL_UART00_CONSOLE is not set ++# CONFIG_SERIAL_SA1100 is not set ++# CONFIG_SERIAL_SA1100_CONSOLE is not set ++# CONFIG_SERIAL_OMAHA is not set ++# CONFIG_SERIAL_OMAHA_CONSOLE is not set ++CONFIG_SERIAL_AT91=y ++CONFIG_SERIAL_AT91_CONSOLE=y ++# CONFIG_SERIAL_8250 is not set ++# CONFIG_SERIAL_8250_CONSOLE is not set ++# CONFIG_SERIAL_8250_EXTENDED is not set ++# CONFIG_SERIAL_8250_MANY_PORTS is not set ++# CONFIG_SERIAL_8250_SHARE_IRQ is not set ++# CONFIG_SERIAL_8250_DETECT_IRQ is not set ++# CONFIG_SERIAL_8250_MULTIPORT is not set ++# CONFIG_SERIAL_8250_HUB6 is not set ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_UNIX98_PTYS is not set ++ ++# ++# I2C support ++# ++CONFIG_I2C=y ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_SCx200_ACB is not set ++# CONFIG_I2C_ALGOPCF is not set ++CONFIG_I2C_AT91=y ++CONFIG_I2C_CHARDEV=y ++CONFIG_I2C_PROC=y ++CONFIG_I2C_DS1307=y ++ ++# ++# L3 serial bus support ++# ++# CONFIG_L3 is not set ++# CONFIG_L3_ALGOBIT is not set ++# CONFIG_L3_BIT_SA1100_GPIO is not set ++# CONFIG_L3_SA1111 is not set ++# CONFIG_BIT_SA1100_GPIO is not set ++ ++# ++# Mice ++# ++# CONFIG_BUSMOUSE is not set ++# CONFIG_MOUSE is not set ++ ++# ++# Joysticks ++# ++# CONFIG_INPUT_GAMEPORT is not set ++# CONFIG_QIC02_TAPE is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_IPMI_PANIC_EVENT is not set ++# CONFIG_IPMI_DEVICE_INTERFACE is not set ++# CONFIG_IPMI_KCS is not set ++# CONFIG_IPMI_WATCHDOG is not set ++ ++# ++# Watchdog Cards ++# ++CONFIG_WATCHDOG=y ++CONFIG_WATCHDOG_NOWAYOUT=y ++# CONFIG_ACQUIRE_WDT is not set ++# CONFIG_ADVANTECH_WDT is not set ++# CONFIG_ALIM1535_WDT is not set ++# CONFIG_ALIM7101_WDT is not set ++# CONFIG_SC520_WDT is not set ++# CONFIG_PCWATCHDOG is not set ++# CONFIG_21285_WATCHDOG is not set ++# CONFIG_977_WATCHDOG is not set ++# CONFIG_SA1100_WATCHDOG is not set ++# CONFIG_EPXA_WATCHDOG is not set ++# CONFIG_OMAHA_WATCHDOG is not set ++CONFIG_AT91_WATCHDOG=y ++# CONFIG_EUROTECH_WDT is not set ++# CONFIG_IB700_WDT is not set ++# CONFIG_WAFER_WDT is not set ++# CONFIG_I810_TCO is not set ++# CONFIG_MIXCOMWD is not set ++# CONFIG_60XX_WDT is not set ++# CONFIG_SC1200_WDT is not set ++# CONFIG_SCx200_WDT is not set ++# CONFIG_SOFT_WATCHDOG is not set ++# CONFIG_W83877F_WDT is not set ++# CONFIG_WDT is not set ++# CONFIG_WDTPCI is not set ++# CONFIG_MACHZ_WDT is not set ++# CONFIG_AMD7XX_TCO is not set ++# CONFIG_SCx200 is not set ++# CONFIG_SCx200_GPIO is not set ++# CONFIG_AMD_PM768 is not set ++# CONFIG_NVRAM is not set ++# CONFIG_RTC is not set ++CONFIG_AT91_RTC=y ++# 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 ++ ++# ++# Direct Rendering Manager (XFree86 DRI support) ++# ++# CONFIG_DRM is not set ++ ++# ++# Multimedia devices ++# ++# CONFIG_VIDEO_DEV is not set ++ ++# ++# File systems ++# ++# CONFIG_QUOTA is not set ++# CONFIG_QFMT_V2 is not set ++# CONFIG_AUTOFS_FS is not set ++# CONFIG_AUTOFS4_FS is not set ++# CONFIG_REISERFS_FS is not set ++# CONFIG_REISERFS_CHECK is not set ++# CONFIG_REISERFS_PROC_INFO 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_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BEFS_DEBUG is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EXT3_FS is not set ++# CONFIG_JBD is not set ++# CONFIG_JBD_DEBUG 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_JFFS2_FS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_TMPFS is not set ++CONFIG_RAMFS=y ++# CONFIG_ISO9660_FS is not set ++# CONFIG_JOLIET is not set ++# CONFIG_ZISOFS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_JFS_DEBUG is not set ++# CONFIG_JFS_STATISTICS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_NTFS_FS is not set ++# CONFIG_NTFS_RW is not set ++# CONFIG_HPFS_FS is not set ++CONFIG_PROC_FS=y ++CONFIG_DEVFS_FS=y ++CONFIG_DEVFS_MOUNT=y ++# 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=y ++# CONFIG_SYSV_FS 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 ++# CONFIG_XFS_FS is not set ++# CONFIG_XFS_QUOTA is not set ++# CONFIG_XFS_RT is not set ++# CONFIG_XFS_TRACE is not set ++# CONFIG_XFS_DEBUG is not set ++ ++# ++# Network File Systems ++# ++# CONFIG_CODA_FS is not set ++# CONFIG_INTERMEZZO_FS is not set ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++# CONFIG_NFS_DIRECTIO is not set ++CONFIG_ROOT_NFS=y ++# CONFIG_NFSD is not set ++# CONFIG_NFSD_V3 is not set ++# CONFIG_NFSD_TCP is not set ++CONFIG_SUNRPC=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++# 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_NLS is not set ++# CONFIG_NCPFS_EXTRAS is not set ++# CONFIG_ZISOFS_FS is not set ++ ++# ++# Partition Types ++# ++# CONFIG_PARTITION_ADVANCED is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_SMB_NLS is not set ++# CONFIG_NLS is not set ++ ++# ++# Multimedia Capabilities Port drivers ++# ++# CONFIG_MCP is not set ++# CONFIG_MCP_SA1100 is not set ++# CONFIG_MCP_UCB1200 is not set ++# CONFIG_MCP_UCB1200_AUDIO is not set ++# CONFIG_MCP_UCB1200_TS is not set ++ ++# ++# USB support ++# ++# CONFIG_USB is not set ++ ++# ++# Support for USB gadgets ++# ++# CONFIG_USB_GADGET is not set ++ ++# ++# Bluetooth support ++# ++# CONFIG_BLUEZ is not set ++ ++# ++# Kernel hacking ++# ++CONFIG_FRAME_POINTER=y ++CONFIG_DEBUG_USER=y ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_NO_PGT_CACHE is not set ++CONFIG_DEBUG_KERNEL=y ++# CONFIG_DEBUG_SLAB is not set ++# CONFIG_MAGIC_SYSRQ is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_WAITQ is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_DEBUG_ERRORS is not set ++CONFIG_DEBUG_LL=y ++# CONFIG_DEBUG_DC21285_PORT is not set ++# CONFIG_DEBUG_CLPS711X_UART2 is not set ++ ++# ++# Library routines ++# ++CONFIG_CRC32=y ++# CONFIG_ZLIB_INFLATE is not set ++# CONFIG_ZLIB_DEFLATE is not set +diff -urN linux-2.4.26/arch/arm/fastfpe/CPDO.S linux-2.4.26-vrs1/arch/arm/fastfpe/CPDO.S +--- linux-2.4.26/arch/arm/fastfpe/CPDO.S 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/fastfpe/CPDO.S 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,682 @@ ++/* ++The FP structure has 4 words reserved for each register, the first is used just ++for the sign in bit 31, the second and third are for the mantissa (unsigned ++integer, high 32 bit first) and the fourth is the exponent (signed integer). ++The mantissa is always normalized. ++ ++If the exponent is 0x80000000, that is the most negative value, the number ++represented is 0 and both mantissa words are also 0. ++ ++If the exponent is 0x7fffffff, that is the biggest positive value, the number ++represented is infinity if the high 32 mantissa bit are also 0, otherwise it is ++a NaN. The low 32 mantissa bit are 0 if the number represented is infinity. ++ ++Decimal and packed decimal numbers are not supported yet. ++ ++The parameters to these functions are r0=destination pointer, r1 and r2 ++source pointers. r4 is the instruction. They may use r0-r8 and r14. They return ++to fastfpe_next, except CPDO_rnf_core which expects the return address in r14. ++*/ ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_adf ++CPDO_adf: ++ ldmia r1,{r1,r3,r5,r7} ++ ldmia r2,{r2,r4,r6,r8} ++ ++ cmp r7,#0x7fffffff ++ cmpne r8,#0x7fffffff ++ beq CPDO_adf_extra ++ ++ cmp r1,r2 ++ bne CPDO_suf_s ++ ++CPDO_adf_s: ++ subs r2,r7,r8 ++ bge CPDO_adf_2nd ++ ++ mov r7,r8 ++ rsb r2,r2,#0 ++ cmp r2,#32 ++ ble CPDO_adf_1st2 ++ ++ sub r2,r2,#32 ++ cmp r2,#32 ++ movgt r2,#32 ++ mov r5,r3,lsr r2 ++ mov r3,#0 ++ b CPDO_adf_add ++ ++CPDO_adf_1st2: ++ rsb r8,r2,#32 ++ mov r5,r5,lsr r2 ++ orr r5,r5,r3,lsl r8 ++ mov r3,r3,lsr r2 @ 1. op normalized ++ b CPDO_adf_add ++ ++CPDO_adf_2nd: ++ cmp r2,#32 ++ ble CPDO_adf_2nd2 ++ ++ sub r2,r2,#32 ++ cmp r2,#32 ++ movgt r2,#32 ++ mov r6,r4,lsr r2 ++ mov r4,#0 ++ b CPDO_adf_add ++ ++CPDO_adf_2nd2: ++ rsb r8,r2,#32 ++ mov r6,r6,lsr r2 ++ orr r6,r6,r4,lsl r8 ++ mov r4,r4,lsr r2 @ 2. op normalized ++ ++CPDO_adf_add: ++ adds r5,r5,r6 ++ adcs r3,r3,r4 @ do addition ++ bcc CPDO_adf_end ++ ++ add r7,r7,#1 ++ movs r3,r3,rrx ++ mov r5,r5,rrx @ correct for overflow ++ ++CPDO_adf_end: ++ cmp r7,#0x20000000 ++ bge CPDO_inf ++ ++ stmia r0,{r1,r3,r5,r7} ++ b fastfpe_next ++ ++CPDO_adf_extra: ++ cmp r7,#0x7fffffff @ was it the 1st ? ++ bne CPDO_infnan_2 @ no it was the 2nd ++ cmp r8,#0x7fffffff @ if 1st, 2nd too ? ++ bne CPDO_infnan_1 @ no only 1st ++ cmp r3,#0 ++ cmpeq r4,#0 ++ bne CPDO_nan_12 ++ b CPDO_inf ++ ++/*---------------------------------------------------------------------------*/ ++ ++CPDO_infnan_1: ++ stmia r0,{r1,r3,r5,r7} ++ b fastfpe_next ++ ++CPDO_infnan_2: ++ stmia r0,{r2,r4,r6,r8} ++ b fastfpe_next ++ ++CPDO_nan_12: ++ orr r2,r3,r4 ++ b CPDO_inf_1 ++ ++CPDO_nan: ++ mov r2,#0x40000000 @ create non signalling NaN ++ b CPDO_inf_1 ++ ++CPDO_inf: ++ mov r2,#0 ++CPDO_inf_1: ++ mov r3,#0 ++ mov r4,#0x7fffffff ++CPDO_store_1234: ++ stmia r0,{r1,r2,r3,r4} ++ b fastfpe_next ++ ++CPDO_zero: ++ mov r1,#0 ++CPDO_zero_1: ++ mov r2,#0 ++ mov r3,#0 ++ mov r4,#0x80000000 ++ stmia r0,{r1,r2,r3,r4} ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_suf ++CPDO_suf: ++ ldmia r1,{r1,r3,r5,r7} ++ ldmia r2,{r2,r4,r6,r8} ++ ++CPDO_suf_l: ++ cmp r7,#0x7fffffff ++ cmpne r8,#0x7fffffff ++ beq CPDO_suf_extra ++ ++ cmp r1,r2 ++ bne CPDO_adf_s ++ ++CPDO_suf_s: ++ subs r2,r7,r8 @ determine greater number ++ bgt CPDO_suf_2nd @ first number is greater ++ blt CPDO_suf_1st @ second number is greater ++ cmp r3,r4 @ also mantissa is important ++ cmpeq r5,r6 ++ bhi CPDO_suf_2nd @ first number is greater ++ beq CPDO_zero ++ ++CPDO_suf_1st: ++ eor r1,r1,#0x80000000 @ second number is greater, invert sign ++ mov r7,r8 ++ rsb r2,r2,#0 ++ cmp r2,#32 ++ ble CPDO_suf_1st2 ++ ++ sub r2,r2,#32 ++ cmp r2,#32 ++ movgt r2,#32 ++ mov r5,r3,lsr r2 ++ mov r3,#0 ++ b CPDO_suf_1st_sub ++ ++CPDO_suf_1st2: ++ rsb r8,r2,#32 ++ mov r5,r5,lsr r2 ++ orr r5,r5,r3,lsl r8 ++ mov r3,r3,lsr r2 @ 1. op normalized ++ ++CPDO_suf_1st_sub: ++ subs r5,r6,r5 @ do subtraction ++ sbc r3,r4,r3 ++ b CPDO_suf_norm ++ ++CPDO_suf_2nd: ++ cmp r2,#32 ++ ble CPDO_suf_2nd2 ++ ++ sub r2,r2,#32 ++ cmp r2,#32 ++ movgt r2,#32 ++ mov r6,r4,lsr r2 ++ mov r4,#0 ++ b CPDO_suf_2nd_sub ++ ++CPDO_suf_2nd2: ++ rsb r8,r2,#32 ++ mov r6,r6,lsr r2 ++ orr r6,r6,r4,lsl r8 ++ mov r4,r4,lsr r2 @ 2. op normalized ++ ++CPDO_suf_2nd_sub: ++ subs r5,r5,r6 ++ sbc r3,r3,r4 @ do subtraction ++ ++CPDO_suf_norm: ++ teq r3,#0 @ normalize 32bit ++ moveq r3,r5 ++ moveq r5,#0 ++ subeq r7,r7,#32 ++ ++ cmp r3,#0x00010000 @ 16bit ++ movcc r3,r3,lsl#16 ++ orrcc r3,r3,r5,lsr#16 ++ movcc r5,r5,lsl#16 ++ subcc r7,r7,#16 ++ ++ cmp r3,#0x01000000 @ 8bit ++ movcc r3,r3,lsl#8 ++ orrcc r3,r3,r5,lsr#24 ++ movcc r5,r5,lsl#8 ++ subcc r7,r7,#8 ++ ++ cmp r3,#0x10000000 @ 4bit ++ movcc r3,r3,lsl#4 ++ orrcc r3,r3,r5,lsr#28 ++ movcc r5,r5,lsl#4 ++ subcc r7,r7,#4 ++ ++ cmp r3,#0x40000000 @ 2bit ++ movcc r3,r3,lsl#2 ++ orrcc r3,r3,r5,lsr#30 ++ movcc r5,r5,lsl#2 ++ subcc r7,r7,#2 ++ ++ cmp r3,#0x80000000 @ 1bit ++ movcc r3,r3,lsl#1 ++ orrcc r3,r3,r5,lsr#31 ++ movcc r5,r5,lsl#1 ++ subcc r7,r7,#1 ++ ++ cmp r7,#0xe0000000 ++ ble CPDO_zero_1 ++ ++ stmia r0,{r1,r3,r5,r7} ++ b fastfpe_next ++ ++CPDO_suf_extra: ++ cmp r7,#0x7fffffff @ was it the 1st ? ++ eorne r2,r2,#0x80000000 @ change sign, might have been INF ++ bne CPDO_infnan_2 @ no it was the 2nd ++ cmp r8,#0x7fffffff @ if 1st, 2nd too ? ++ bne CPDO_infnan_1 @ no only 1st ++ cmp r3,#0 ++ cmpeq r4,#0 ++ bne CPDO_nan_12 ++ b CPDO_nan @ here is difference with adf ! ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_rsf ++CPDO_rsf: ++ mov r3,r2 ++ ldmia r1,{r2,r4,r6,r8} ++ ldmia r3,{r1,r3,r5,r7} ++ b CPDO_suf_l ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_muf ++CPDO_muf: ++ ldmia r1,{r1,r3,r5,r7} ++ ldmia r2,{r2,r4,r6,r8} ++ ++ cmp r7,#0x7fffffff ++ cmpne r8,#0x7fffffff ++ beq CPDO_muf_extra ++ ++ eor r1,r1,r2 ++ adds r8,r7,r8 ++ bvs CPDO_zero_1 ++ ++ umull r7,r2,r3,r4 ++ umull r14,r3,r6,r3 ++ adds r7,r7,r3 @ r2|r7|r14 = r2|r7|#0 + #0|r3|r14 ++ adc r2,r2,#0 ++ umull r4,r3,r5,r4 ++ adds r14,r14,r4 @ r2|r7|r14 += #0|r3|r4 ++ adcs r7,r7,r3 ++ adc r2,r2,#0 ++ umull r4,r3,r5,r6 ++ adds r14,r14,r3 @ r2|r7|r14 += #0|#0|r3 ++ adcs r7,r7,#0 ++ adcs r2,r2,#0 ++ ++ bpl CPDO_muf_norm ++ ++ add r8,r8,#1 ++ b CPDO_muf_end ++ ++CPDO_muf_norm: ++ adds r14,r14,r14 ++ adcs r7,r7,r7 ++ adcs r2,r2,r2 ++ ++CPDO_muf_end: ++ cmp r8,#0x20000000 ++ bge CPDO_inf ++ cmp r8,#0xe0000000 ++ ble CPDO_zero_1 ++ stmia r0,{r1,r2,r7,r8} ++ b fastfpe_next ++ ++CPDO_muf_extra: ++ cmp r7,#0x7fffffff @ was it the first? ++ bne CPDO_muf_extra_2nd @ no, so it was the second ++ cmp r8,#0x7fffffff @ yes, second too? ++ bne CPDO_muf_extra_1st @ no, only first ++ orr r3,r3,r4 @ if both inf -> inf, otherwise nan ++ eor r1,r1,r2 @ sign for the inf case ++ b CPDO_infnan_1 ++ ++CPDO_muf_extra_1st: ++ cmp r3,#0 @ is it a nan? ++ bne CPDO_infnan_1 ++ cmp r8,#0x80000000 @ is the second 0? ++ beq CPDO_nan ++ eor r1,r1,r2 @ correct sign for inf ++ b CPDO_inf ++ ++CPDO_muf_extra_2nd: ++ cmp r4,#0 @ is it a nan? ++ bne CPDO_infnan_2 ++ cmp r7,#0x80000000 @ is the first 0? ++ beq CPDO_nan ++ eor r1,r1,r2 @ correct sign for inf ++ b CPDO_inf ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_dvf ++CPDO_dvf: ++ ldmia r1,{r1,r3,r5,r7} ++ ldmia r2,{r2,r4,r6,r8} ++ ++CPDO_dvf_l: ++ cmp r7,#0x7fffffff ++ cmpne r8,#0x7fffffff ++ beq CPDO_dvf_extra ++ cmp r8,#0x80000000 ++ beq CPDO_dvf_by0 ++ ++ eor r1,r1,r2 ++ cmp r7,#0x80000000 ++ beq CPDO_zero_1 ++ ++ sub r8,r7,r8 ++ ++ mov r2,#0 ++ mov r7,#1 ++ ++ cmp r3,r4 ++ cmpeq r5,r6 ++ bcs CPDO_dvf_loop_ ++ ++ sub r8,r8,#1 ++ ++CPDO_dvf_loop: ++ adds r5,r5,r5 ++ adcs r3,r3,r3 ++ bcs CPDO_dvf_anyway ++CPDO_dvf_loop_: ++ subs r5,r5,r6 ++ sbcs r3,r3,r4 ++ bcs CPDO_dvf_okay ++ ++ adds r5,r5,r6 ++ adc r3,r3,r4 ++ adds r7,r7,r7 ++ adcs r2,r2,r2 ++ bcc CPDO_dvf_loop ++ b CPDO_dvf_end ++ ++CPDO_dvf_anyway: ++ adcs r7,r7,r7 ++ adcs r2,r2,r2 ++ bcs CPDO_dvf_end ++ subs r5,r5,r6 ++ sbc r3,r3,r4 ++ b CPDO_dvf_loop ++ ++CPDO_dvf_okay: ++ adcs r7,r7,r7 ++ adcs r2,r2,r2 ++ bcc CPDO_dvf_loop ++ ++CPDO_dvf_end: ++ b CPDO_muf_end ++ ++CPDO_dvf_by0: ++ cmp R7,#0x80000000 ++ beq CPDO_nan @ first also 0 -> nan ++ eor r1,r1,r2 @ otherwise calculatesign for inf ++ b CPDO_inf ++ ++CPDO_dvf_extra: ++ cmp r7,#0x7fffffff @ was it the first? ++ bne CPDO_dvf_extra_2nd @ no, so it was the second ++ cmp r8,#0x7fffffff @ yes, second too? ++ bne CPDO_dvf_extra_1st @ no, only first ++ orrs r3,r3,r4 ++ beq CPDO_nan @ if both inf -> create nan ++ b CPDO_nan_12 @ otherwise keep nan ++ ++CPDO_dvf_extra_1st: ++ eor r1,r1,r2 @ correct sign for inf ++ b CPDO_infnan_1 ++ ++CPDO_dvf_extra_2nd: ++ cmp r4,#0 @ is it a nan? ++ bne CPDO_infnan_2 ++ eor r1,r1,r2 @ correct sign for zero ++ b CPDO_zero_1 ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_rdf ++CPDO_rdf: ++ mov r3,r2 ++ ldmia r1,{r2,r4,r6,r8} ++ ldmia r3,{r1,r3,r5,r7} ++ b CPDO_dvf_l ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_rmf ++CPDO_rmf: ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_mvf ++CPDO_mvf: ++ ldmia r2,{r1,r2,r3,r4} ++ stmia r0,{r1,r2,r3,r4} ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_mnf ++CPDO_mnf: ++ ldmia r2,{r1,r2,r3,r4} ++ eor r1,r1,#0x80000000 ++ stmia r0,{r1,r2,r3,r4} ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_abs ++CPDO_abs: ++ ldmia r2,{r1,r2,r3,r4} ++ bic r1,r1,#0x80000000 ++ stmia r0,{r1,r2,r3,r4} ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_sqt ++CPDO_sqt: ++ ldmia r2,{r1,r2,r3,r4} ++ cmp r1,#0 ++ bne CPDO_nan ++ cmp r4,#0x7fffffff ++ beq CPDO_store_1234 ++ ++ tst r4,r4,lsr#1 @carry=exponent bit 0 ++ bcc CPDO_sqt_exponenteven ++ adds r3,r3,r3 ++ adcs r2,r2,r2 @carry is needed in loop! ++CPDO_sqt_exponenteven: ++ mov r4,r4,asr #1 ++ str r4,[r0,#12] ++ ++ mov r4,#0x80000000 ++ mov r5,#0 ++ sub r2,r2,#0x80000000 ++ ++ mov r8,#0x40000000 ++ mov r14,#0x80000000 ++ ++ mov r1,#1 ++ b CPDO_sqt_loop1_first ++CPDO_sqt_loop1: ++ adds r3,r3,r3 ++ adcs r2,r2,r2 ++CPDO_sqt_loop1_first: ++ add r6,r4,r8,lsr r1 @r7 const = r5 ++ bcs CPDO_sqt_loop1_1 ++ cmp r2,r6 ++ cmpeq r3,r5 @r5 for r7 ++ bcc CPDO_sqt_loop1_0 ++CPDO_sqt_loop1_1: ++ orr r4,r4,r14,lsr r1 ++ subs r3,r3,r5 @r5 for r7 ++ sbc r2,r2,r6 ++CPDO_sqt_loop1_0: ++ add r1,r1,#1 ++ cmp r1,#30 ++ ble CPDO_sqt_loop1 ++ ++ adds r3,r3,r3 ++ adcs r2,r2,r2 ++ bcs CPDO_sqt_between_1 ++ adds r7,r5,#0x80000000 ++ adc r6,r4,#0 ++ cmp r2,r6 ++ cmpeq r3,r7 ++ bcc CPDO_sqt_between_0 ++CPDO_sqt_between_1: ++ orr r4,r4,#0x00000001 ++ subs r3,r3,r5 ++ sbc r2,r2,r4 ++ subs r3,r3,#0x80000000 ++ sbc r2,r2,#0 ++CPDO_sqt_between_0: ++ mov r1,#0 ++ ++CPDO_sqt_loop2: ++ adds r3,r3,r3 ++ adcs r2,r2,r2 ++ bcs CPDO_sqt_loop2_1 ++ adds r7,r5,r8,lsr r1 ++ adc r6,r4,#0 ++ cmp r2,r6 ++ cmpeq r3,r7 ++ bcc CPDO_sqt_loop2_0 ++CPDO_sqt_loop2_1: ++ orr r5,r5,r14,lsr r1 ++ subs r3,r3,r5 ++ sbc r2,r2,r4 ++ subs r3,r3,r8,lsr r1 ++ sbc r2,r2,#0 ++CPDO_sqt_loop2_0: ++ add r1,r1,#1 ++ cmp r1,#30 ++ ble CPDO_sqt_loop2 ++ ++ adds r3,r3,r3 ++ adcs r2,r2,r2 ++ bcs CPDO_sqt_after_1 ++ cmp r2,r6 ++ cmpeq r3,r7 ++ bcc CPDO_sqt_after_0 ++CPDO_sqt_after_1: ++ orr r5,r5,#0x00000001 ++CPDO_sqt_after_0: ++ ++ mov r1,#0 ++ stmia r0,{r1,r4,r5} ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_rnd ++CPDO_rnd: ++ ldmia r2,{r1,r2,r3,r5} ++ bl CPDO_rnd_core ++ ++CPDO_rnd_store: ++ stmia r0,{r1,r2,r3,r5} ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDO_rnd_core ++CPDO_rnd_core: ++ and r4,r4,#0x00000060 ++ add pc,pc,r4,lsr#3 ++ mov r0,r0 ++ b CPDO_rnd_N ++ b CPDO_rnd_P ++ b CPDO_rnd_M ++ b CPDO_rnd_Z ++ ++CPDO_rnd_N: ++ cmp r5,#-1 ++ blt CPDO_rnd_zero ++ cmp r5,#63 ++ movge pc,r14 ++ mov r4,#0x40000000 ++ cmp r5,#31 ++ bge CPDO_rnd_N_2 ++ ++ adds r2,r2,r4,lsr r5 ++ bcc CPDO_rnd_end ++ b CPDO_rnd_end_norm ++ ++CPDO_rnd_N_2: ++CPDO_rnd_P_2: ++ sub r6,r5,#32 ++ adds r3,r3,r4,ror r6 @ror ist needed to handle a -1 correctly ++ adcs r2,r2,#0 ++ bcc CPDO_rnd_end ++ b CPDO_rnd_end_norm ++ ++CPDO_rnd_P: ++ tst r1,#0x80000000 ++ bne CPDO_rnd_M_entry ++CPDO_rnd_P_entry: ++ cmp r5,#0 ++ blt CPDO_rnd_P_small ++ cmp r5,#63 ++ movge pc,r14 ++ mov r4,#0x7fffffff ++ cmp r5,#32 ++ bge CPDO_rnd_P_2 ++ ++ adds r3,r3,#0xffffffff ++ adcs r2,r2,r4,lsr r5 ++ bcc CPDO_rnd_end ++ b CPDO_rnd_end_norm ++ ++CPDO_rnd_P_small: ++ cmp r5,#0x80000000 ++ moveq pc,r14 ++ b CPDO_rnd_one ++ ++CPDO_rnd_M: ++ tst r1,#0x80000000 ++ bne CPDO_rnd_P_entry ++CPDO_rnd_M_entry: ++ cmp r5,#0 ++ blt CPDO_rnd_zero ++ cmp r5,#63 ++ movge pc,r14 ++ ++ b CPDO_rnd_end ++ ++CPDO_rnd_Z: ++ cmp r5,#0 ++ blt CPDO_rnd_zero ++ cmp r5,#63 ++ movge pc,r14 ++ b CPDO_rnd_end ++ ++CPDO_rnd_end_norm: ++ add r5,r5,#1 ++ movs r2,r2,rrx ++ mov r3,r3,rrx ++CPDO_rnd_end: ++ rsbs r4,r5,#31 ++ bmi CPDO_rnd_end_2 ++ mov r3,#0 ++ mov r2,r2,lsr r4 ++ mov r2,r2,lsl r4 ++ mov pc,r14 ++ ++CPDO_rnd_end_2: ++ rsb r4,r5,#63 ++ mov r3,r3,lsr r4 ++ mov r3,r3,lsl r4 ++ mov pc,r14 ++ ++CPDO_rnd_one: ++ mov r2,#0x80000000 ++ mov r3,#0 ++ mov r5,#0 ++ mov pc,r14 ++ ++CPDO_rnd_zero: ++ mov r1,#0 ++ mov r2,#0 ++ mov r3,#0 ++ mov r5,#0x80000000 ++ mov pc,r14 ++ ++/*---------------------------------------------------------------------------*/ +diff -urN linux-2.4.26/arch/arm/fastfpe/CPDT.S linux-2.4.26-vrs1/arch/arm/fastfpe/CPDT.S +--- linux-2.4.26/arch/arm/fastfpe/CPDT.S 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/fastfpe/CPDT.S 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,430 @@ ++/* ++The FP structure has 4 words reserved for each register, the first is used just ++for the sign in bit 31, the second and third are for the mantissa (unsigned ++integer, high 32 bit first) and the fourth is the exponent (signed integer). ++The mantissa is always normalized. ++ ++If the exponent is 0x80000000, that is the most negative value, the number ++represented is 0 and both mantissa words are also 0. ++ ++If the exponent is 0x7fffffff, that is the biggest positive value, the number ++represented is infinity if the high 32 mantissa bit are also 0, otherwise it is ++a NaN. The low 32 mantissa bit are 0 if the number represented is infinity. ++ ++Decimal and packed decimal numbers are not supported yet. ++*/ ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDT_load_single ++CPDT_load_single: ++ ldr r1,[r6] ++ ++ and r2,r1,#0x80000000 @ r2 = sign ++ ++ mov r5,r1,lsr#23 ++ bics r5,r5,#0x100 ++ beq CPDT_ls_e0 @ exponent = 0; zero/denormalized ++ teq r5,#255 ++ beq CPDT_ls_e255 @ exponent = 255; infinity/NaN ++ ++ sub r5,r5,#127 @ r5 = exponent, remove normalized bias ++ ++ mov r3,r1,lsl#8 ++ orr r3,r3,#0x80000000 ++ mov r4,#0 @ r3,r4 = mantissa ++ ++ stmia r0,{r2-r5} ++ b fastfpe_next ++ ++CPDT_ls_e0: ++ movs r3,r1,lsl#9 ++ beq CPDT_load_zero ++ ++ mov r5,#-127 ++ ++CPDT_ls_e0_norm: ++ tst r3,#0x80000000 ++ subeq r5,r5,#1 ++ moveq r3,r3,lsl#1 ++ beq CPDT_ls_e0_norm ++ ++ mov r4,#0 ++ stmia r0,{r2-r5} ++ b fastfpe_next ++ ++CPDT_ls_e255: ++ mov r3,r1,lsl#9 ++ mov r4,#0 ++ mov r5,#0x7fffffff ++ stmia r0,{r2-r5} ++ b fastfpe_next ++ ++CPDT_load_zero: ++ mov r3,#0 ++ mov r4,#0 ++ mov r5,#0x80000000 ++ stmia r0,{r2-r5} ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDT_load_double ++CPDT_load_double: ++ ldr r1,[r6] ++ ldr r6,[r6,#4] ++ ++ and r2,r1,#0x80000000 @ r2 = sign ++ ++ mov r5,r1,lsr#20 ++ bics r5,r5,#0x800 ++ beq CPDT_ld_e0 @ exponent = 0; zero/denormalized ++ add r4,r5,#1 ++ teq r4,#2048 ++ beq CPDT_ld_e2047 @ exponent = 2047; infinity/NaN ++ ++ add r5,r5,#1 ++ sub r5,r5,#1024 @ r5 = exponent, remove normalized bias ++ ++ mov r3,r1,lsl#11 ++ orr r3,r3,#0x80000000 ++ orr r3,r3,r6,lsr #21 ++ mov r4,r6,lsl#11 @ r3,r4 = mantissa ++ ++ stmia r0,{r2-r5} ++ b fastfpe_next ++ ++CPDT_ld_e0: ++ mov r3,r1,lsl#12 ++ orr r3,r3,r6,lsr#20 ++ movs r4,r6,lsl#12 ++ teqeq r3,#0 ++ beq CPDT_load_zero ++ ++ mov r5,#1 ++ sub r5,r5,#1024 ++ ++CPDT_ld_e0_norm: ++ tst r3,#0x80000000 ++ subeq r5,r5,#1 ++ moveqs r4,r4,lsl#1 ++ adceq r3,r3,r3 ++ beq CPDT_ld_e0_norm ++ ++ stmia r0,{r2-r5} ++ b fastfpe_next ++ ++CPDT_ld_e2047: ++ mov r3,r1,lsl#12 ++ orr r3,r3,r6,lsr#1 ++ bic r6,r6,#0x80000000 ++ orr r3,r3,r6 @ to get all fraction bits ! ++ mov r4,#0 ++ mov r5,#0x7fffffff ++ stmia r0,{r2-r5} ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDT_load_extended ++CPDT_load_extended: ++ ldr r1,[r6] ++ ldr r3,[r6,#4] ++ ldr r4,[r6,#8] ++ ++ and r2,r1,#0x80000000 ++ bics r5,r1,#0x80000000 ++ beq CPDT_le_e0 ++ add r1,r5,#1 ++ teq r4,#32768 ++ beq CPDT_le_e32767 ++ ++ add r5,r5,#1 ++ sub r5,r5,#16384 ++ ++ stmia r0,{r2-r5} ++ b fastfpe_next ++ ++CPDT_le_e0: ++ teq r3,#0 ++ teqeq r4,#0 ++ beq CPDT_load_zero ++ ++ mov r5,#2 ++ sub r5,r5,#16384 ++ b CPDT_ld_e0_norm ++ ++CPDT_le_e32767: ++ mov r3,r3,lsl#1 ++ orr r3,r3,r4,lsr#1 ++ bic r4,r4,#0x80000000 ++ orr r3,r3,r4 ++ mov r5,#0x7fffffff ++ stmia r0,{r2-r5} ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDT_load_decimal ++CPDT_load_decimal: ++ ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDT_store_single ++CPDT_store_single: ++ ldmia r0,{r1-r4} ++ ++ cmp r4,#-127 ++ ble CPDT_ss_e0 ++ cmp r4,#128 ++ bge CPDT_ss_e255 ++ ++ adds r2,r2,#1<<7 @ round to nearest ++ bcs CPDT_ss_rnd_ovfl @ very very seldom taken ++ ++CPDT_ss_store: ++ add r4,r4,#127 ++ orr r1,r1,r4,lsl#23 ++ ++ bic r2,r2,#0x80000000 ++ orr r1,r1,r2,lsr#8 ++ ++ str r1,[r6] ++ b fastfpe_next ++ ++CPDT_ss_rnd_ovfl: ++ add r4,r4,#1 ++ cmp r4,#128 ++ bge CPDT_ss_e255 ++ ++ mov r2,#0x80000000 ++ mov r3,#0 ++ b CPDT_ss_store ++ ++CPDT_ss_e0: ++ cmp r4,#-150 ++ ble CPDT_ss_zero ++ ++ add r4,r4,#126 ++CPDT_ss_unnormalize: ++ mov r2,r2,lsr#1 ++ adds r4,r4,#1 ++ bne CPDT_ss_unnormalize ++ ++ orr r1,r1,r2,lsr#8 ++ ++CPDT_ss_zero: ++ str r1,[r6] ++ b fastfpe_next ++ ++CPDT_ss_e255: ++ cmp r4,#0x7fffffff ++ bne CPDT_ss_inf ++ cmp r2,#0 ++ beq CPDT_ss_inf ++ ++ orr r1,r1,#0x00200000 @ for safety so that it is not INF ++ orr r1,r1,r2,lsr#9 @ get highest bit of mantissa ++ ++CPDT_ss_inf: ++ orr r1,r1,#0x7f000000 ++ orr r1,r1,#0x00800000 ++ str r1,[r6] ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDT_store_double ++CPDT_store_double: ++ ldmia r0,{r1-r4} ++ ++ cmp r4,#1024 @ this check has to be first, or ++ bge CPDT_sd_e2047 @ overflow can occur on second ! ++ add r0,r4,#3 ++ cmp r0,#-1023+3 @ cmp with -1023 ++ ble CPDT_sd_e0 ++ ++ adds r3,r3,#1<<10 @ round to nearest ++ adcs r2,r2,#0 ++ bcs CPDT_sd_rnd_ovfl @ very very seldom taken ++ ++CPDT_sd_store: ++ sub r4,r4,#1 ++ add r4,r4,#1024 ++ orr r1,r1,r4,lsl#20 ++ ++ bic r2,r2,#0x80000000 ++ orr r1,r1,r2,lsr#11 ++ ++ mov r2,r2,lsl#21 ++ orr r2,r2,r3,lsr#11 ++ ++ stmia r6,{r1,r2} ++ b fastfpe_next ++ ++CPDT_sd_rnd_ovfl: ++ add r4,r4,#1 ++ cmp r4,#1024 ++ bge CPDT_sd_e2047 ++ ++ mov r2,#0x80000000 ++ mov r3,#0 ++ b CPDT_sd_store ++ ++CPDT_sd_e0: ++ add r0,r4,#1075-1024 ++ cmp r0,#-1024 ++ ble CPDT_sd_zero ++ ++ add r4,r4,#1024 ++ sub r4,r4,#2 ++CPDT_sd_unnormalize: ++ movs r2,r2,lsr#1 ++ mov r3,r3,rrx ++ adds r4,r4,#1 ++ bne CPDT_sd_unnormalize ++ ++ orr r1,r1,r2,lsr#11 ++ mov r2,r2,lsl#21 ++ orr r2,r2,r3,lsr#11 ++ ++ stmia r6,{r1,r2} ++ b fastfpe_next ++ ++CPDT_sd_zero: ++ mov r2,#0 ++ stmia r6,{r1,r2} ++ b fastfpe_next ++ ++CPDT_sd_e2047: ++ cmp r4,#0x7fffffff ++ bne CPDT_sd_inf ++ cmp r2,#0 ++ beq CPDT_sd_inf ++ ++ orr r1,r1,#0x00040000 @ for safety so that it is not INF ++ orr r1,r1,r2,lsr#12 @ get highest bit of mantissa ++ ++CPDT_sd_inf: ++ orr r1,r1,#0x7f000000 ++ orr r1,r1,#0x00f00000 ++ stmia r6,{r1,r2} ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDT_store_extended ++CPDT_store_extended: ++ ldmia r0,{r1-r4} ++ ++ cmp r4,#16384 @ this check has to be first, or ++ bge CPDT_se_e32767 @ overflow can occur with second ! ++ add r0,r4,#63 ++ cmp r0,#-16383+63 ++ ble CPDT_se_e0 ++ ++ sub r4,r4,#1 ++ add r4,r4,#16384 ++ orr r1,r1,r4 ++ ++ stmia r6,{r1-r3} ++ b fastfpe_next ++ ++CPDT_se_e0: ++ add r0,r4,#16446-16384 ++ cmp r0,#-16384 ++ ble CPDT_se_zero ++ ++ add r4,r4,#16384 ++ sub r4,r4,#2 ++CPDT_se_unnormalize: ++ movs r2,r2,lsr#1 ++ mov r3,r3,rrx ++ adds r4,r4,#1 ++ bne CPDT_se_unnormalize ++ ++ stmia r6,{r1-r3} ++ b fastfpe_next ++ ++CPDT_se_zero: ++ mov r2,#0 ++ mov r3,#0 ++ stmia r6,{r1-r3} ++ b fastfpe_next ++ ++CPDT_se_e32767: ++ cmp r4,#0x7fffffff ++ bne CPDT_se_inf ++ cmp r2,#0 ++ beq CPDT_se_inf ++ ++ mov r2,r2,lsl#1 ++ orr r2,r2,#0x20000000 ++ ++CPDT_se_inf: ++ orr r1,r1,#0x00007f00 ++ orr r1,r1,#0x000000ff ++ stmia r6,{r1-r3} ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDT_store_decimal ++CPDT_store_decimal: ++ ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDT_sfm ++CPDT_sfm: ++ add r2,r10,r0,lsr#8 ++ ldr r4,[r2,#0] ++ ldr r3,[r2,#4] ++ bic r3,r3,#0x80000000 ++ orr r3,r3,r4 ++ str r3,[r6],#4 ++ ldr r3,[r2,#8] ++ str r3,[r6],#4 ++ ldr r3,[r2,#12] ++ str r3,[r6],#4 ++ ++ add r0,r0,#1<<12 ++ and r0,r0,#7<<12 ++ subs r1,r1,#1 ++ bne CPDT_sfm ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPDT_lfm ++CPDT_lfm: ++ add r2,r10,r0,lsr#8 ++ ldr r4,[r6],#4 ++ and r3,r4,#0x80000000 ++ str r3,[r2,#0] ++ ldr r3,[r6],#4 ++ str r3,[r2,#8] ++ ldr r3,[r6],#4 ++ str r3,[r2,#12] ++ ++ cmp r3,#0x80000000 @ does the exp indicate zero? ++ biceq r4,r4,#0x80000000 @ if so, indicate 'denormalized' ++ beq CPDT_lfm_storer4 ++ cmp r3,#0x7fffffff @ does the exp indicate inf or NaN? ++ biceq r4,r4,#0x80000000 @ if so, indicate 'denormalized' ++ beq CPDT_lfm_storer4 ++ orrne r4,r4,#0x80000000 @ otherwise, set normalized bit ++ ++CPDT_lfm_storer4: ++ str r4,[r2,#4] ++ ++ add r0,r0,#1<<12 ++ and r0,r0,#7<<12 ++ subs r1,r1,#1 ++ bne CPDT_lfm ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ +diff -urN linux-2.4.26/arch/arm/fastfpe/CPRT.S linux-2.4.26-vrs1/arch/arm/fastfpe/CPRT.S +--- linux-2.4.26/arch/arm/fastfpe/CPRT.S 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/fastfpe/CPRT.S 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,185 @@ ++/* ++The FP structure has 4 words reserved for each register, the first is used ++just ++for the sign in bit 31, the second and third are for the mantissa (unsigned ++integer, high 32 bit first) and the fourth is the exponent (signed integer). ++The mantissa is always normalized. ++ ++If the exponent is 0x80000000, that is the most negative value, the number ++represented is 0 and both mantissa words are also 0. ++ ++If the exponent is 0x7fffffff, that is the biggest positive value, the ++number ++represented is infinity if the high 32 mantissa bit are also 0, otherwise it ++is ++a NaN. The low 32 mantissa bit are 0 if the number represented is infinity. ++ ++Decimal and packed decimal numbers are not supported yet. ++*/ ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .text ++ .globl CPRT_flt ++CPRT_flt: ++ add r0,r13,r0,lsr#10 ++ ldr r2,[r0] ++ mov r3,#0 ++ cmp r2,#0 ++ beq CPRT_flt_zero ++ ++ ands r0,r2,#0x80000000 ++ rsbne r2,r2,#0 ++ mov r4,#31 ++ ++ cmp r2,#0x00010000 ++ movcc r2,r2,lsl#16 ++ subcc r4,r4,#16 ++ ++ cmp r2,#0x01000000 ++ movcc r2,r2,lsl#8 ++ subcc r4,r4,#8 ++ ++ cmp r2,#0x10000000 ++ movcc r2,r2,lsl#4 ++ subcc r4,r4,#4 ++ ++ cmp r2,#0x40000000 ++ movcc r2,r2,lsl#2 ++ subcc r4,r4,#2 ++ ++ cmp r2,#0x80000000 ++ movcc r2,r2,lsl#1 ++ subcc r4,r4,#1 ++ ++ stmia r1,{r0,r2,r3,r4} ++ b fastfpe_next ++ ++CPRT_flt_zero: ++ mov r0,#0 ++ mov r4,#0x80000000 ++ stmia r1,{r0,r2,r3,r4} ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPRT_fix ++CPRT_fix: ++ ldmia r2,{r1,r2,r3,r5} ++ bl CPDO_rnd_core ++ ++CPRT_back: ++ add r0,r13,r0,lsr#10 ++ cmp r5,#0 ++ blt CPRT_int_zero ++ cmp r5,#30 ++ bgt CPRT_overflow ++ ++ rsb r5,r5,#31 ++ mov r2,r2,lsr r5 ++ tst r1,#0x80000000 ++ rsbne r2,r2,#0 ++ ++ str r2,[r0] ++ b fastfpe_next ++ ++CPRT_int_zero: ++ mov r2,#0 ++ str r2,[r0] ++ b fastfpe_next ++ ++CPRT_overflow: ++ mov r2,#0x80000000 ++ tst r1,#0x80000000 ++ subeq r2,r2,#1 ++ str r2,[r0] ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPRT_wfs ++CPRT_wfs: ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPRT_rfs ++CPRT_rfs: ++ add r0,r13,r0,lsr#10 ++ mov r1,#0x02000000 @ Software Emulation, not Acorn FPE ++ str r1,[r0] ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPRT_cmf ++CPRT_cmf: ++ ldmia r1,{r1,r3,r5,r7} ++ ldmia r2,{r2,r4,r6,r8} ++ ++CPRT_cmf_e: ++ ldr r0,[r13,#16*4] ++ ++ cmp r7,#0x7fffffff ++ bic r0,r0,#0xf0000000 ++ ++ cmpeq r3,#0xffffffff ++ beq CPRT_cmf_unordered ++ cmp r8,#0x7fffffff ++ cmpeq r4,#0xffffffff ++ beq CPRT_cmf_unordered ++ ++ cmp r1,r2 ++ beq CPRT_cmf_equalsign ++ b CPRT_cmf_sign ++ ++CPRT_cmf_equalsign: ++ cmp r7,r8 ++ beq CPRT_cmf_equalexponent ++ bgt CPRT_cmf_sign ++ b CPRT_cmf_signb ++ ++CPRT_cmf_equalexponent: ++ cmp r3,r4 ++ cmpeq r5,r6 ++ beq CPRT_cmf_equal ++ bhi CPRT_cmf_sign ++ b CPRT_cmf_signb ++ ++CPRT_cmf_sign: ++ cmp r7,#0x80000000 @ (0.0 == -0.0)? ++ cmpeq r7,r8 ++ beq CPRT_cmf_equal ++ tst r1,#0x80000000 ++ orreq r0,r0,#0x20000000 ++ orrne r0,r0,#0x80000000 ++ str r0,[r13,#16*4] ++ b fastfpe_next ++ ++CPRT_cmf_signb: ++ tst r1,#0x80000000 ++ orrne r0,r0,#0x20000000 ++ orreq r0,r0,#0x80000000 ++ str r0,[r13,#16*4] ++ b fastfpe_next ++ ++CPRT_cmf_equal: ++ orr r0,r0,#0x60000000 ++ str r0,[r13,#16*4] ++ b fastfpe_next ++ ++CPRT_cmf_unordered: ++ orr r0,r0,#0x10000000 ++ str r0,[r13,#16*4] ++ b fastfpe_next ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl CPRT_cnf ++CPRT_cnf: ++ ldmia r1,{r1,r3,r5,r7} ++ ldmia r2,{r2,r4,r6,r8} ++ eor r2,r2,#0x80000000 ++ b CPRT_cmf_e ++ ++/*---------------------------------------------------------------------------*/ +diff -urN linux-2.4.26/arch/arm/fastfpe/Makefile linux-2.4.26-vrs1/arch/arm/fastfpe/Makefile +--- linux-2.4.26/arch/arm/fastfpe/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/fastfpe/Makefile 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,25 @@ ++# ++# linux/arch/arm/fastfpe/Makefile ++# ++# Copyright (C) Peter Teichmann ++# ++ ++O_TARGET := fast-math-emu.o ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++fastfpe-objs := module.o entry.o CPDO.o CPRT.o CPDT.o ++ ++list-multi := fastfpe.o ++ ++obj-$(CONFIG_FPE_FASTFPE) += fastfpe.o ++ ++USE_STANDARD_AS_RULE := true ++ ++include $(TOPDIR)/Rules.make ++ ++fastfpe.o: $(fastfpe-objs) ++ $(LD) -r -o $@ $(fastfpe-objs) +diff -urN linux-2.4.26/arch/arm/fastfpe/entry.S linux-2.4.26-vrs1/arch/arm/fastfpe/entry.S +--- linux-2.4.26/arch/arm/fastfpe/entry.S 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/fastfpe/entry.S 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,295 @@ ++/* ++At entry the registers contain the following information: ++ ++r14 return address for undefined exception return ++r9 return address for return from exception ++r13 user registers on stack, offset 0 up to offset 4*15 contains ++ registers r0..15, then the psr ++r10 FP workspace 35 words (init, reg[8][4], fpsr, fpcr) ++ ++*/ ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .data ++fp_const: ++ .word 0, 0x00000000, 0, 0x80000000 @ 0 ++ .word 0, 0x80000000, 0, 0 @ 1 ++ .word 0, 0x80000000, 0, 1 @ 2 ++ .word 0, 0xc0000000, 0, 1 @ 3 ++ .word 0, 0x80000000, 0, 2 @ 4 ++ .word 0, 0xa0000000, 0, 2 @ 5 ++ .word 0, 0x80000000, 0, -1 @ 0.5 ++ .word 0, 0xa0000000, 0, 3 @ 10 ++fp_undef: ++ .word 0 ++fp_cond: ++ .word 0xf0f0 @ eq ++ .word 0x0f0f @ ne ++ .word 0xcccc @ cs ++ .word 0x3333 @ cc ++ .word 0xff00 @ mi ++ .word 0x00ff @ pl ++ .word 0xaaaa @ vs ++ .word 0x5555 @ vc ++ .word 0x0c0c @ hi ++ .word 0xf3f3 @ ls ++ .word 0xaa55 @ ge ++ .word 0x55aa @ lt ++ .word 0x0a05 @ gt ++ .word 0xf5fa @ le ++ .word 0xffff @ al ++ .word 0x0000 @ nv ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .text ++ .globl fastfpe_enter ++fastfpe_enter: ++ ldr r4,=fp_undef ++ str r14,[r4] @ to free one register ++ add r10,r10,#4 @ to make the code simpler ++ ldr r4,[r13,#60] @ r4=saved PC ++ ldr r4,[r4,#-4] @ r4=trapped instruction ++ and r1,r4,#0x00000f00 @ r1=coprocessor << 8 ++next_enter: ++ cmp r1,#1<<8 @ copro 1 ? ++ beq copro_1 ++ cmp r1,#2<<8 ++ movne pc,r14 ++ ++copro_2: ++ and r1,r4,#0x0f000000 ++ cmp r1,#0x0c000000 @ CPDT with post indexing ++ cmpne r1,#0x0d000000 @ CPDT with pre indexing ++ beq CPDT_M_enter ++ mov pc,r14 ++ ++copro_1: ++ and r1,r4,#0x0f000000 ++ cmp r1,#0x0e000000 @ CPDO ++ beq CPDO_CPRT_enter ++ cmp r1,#0x0c000000 @ CPDT with post indexing ++ cmpne r1,#0x0d000000 @ CPDT with pre indexing ++ beq CPDT_1_enter ++ mov pc,r14 ++ ++/*---------------------------------------------------------------------------*/ ++ ++ .globl fastfpe_next ++fastfpe_next: ++ ldr r5,[r13,#60] ++next_after_cond: ++__x1: ++ ldrt r4,[r5],#4 ++ ++ ldr r0,=fp_cond @ check condition of next instruction ++ ldr r1,[r13,#64] @ psr containing flags ++ mov r2,r4,lsr#28 ++ mov r1,r1,lsr#28 ++ ldr r0,[r0,r2,lsl#2] ++ mov r0,r0,lsr r1 ++ tst r0,#1 ++ beq next_after_cond @ must not necessarily have been an ++ @ FP instruction ! ++ and r1,r4,#0x0f000000 @ Test for copro instruction ++ cmp r1,#0x0c000000 ++ rsbgts r0,r1,#0x0e000000 @ cmpgt #0x0e000000,r1 ++ movlt pc,r9 @ next is no copro instruction, return ++ ++ ands r1,r4,#0x00000f00 @ r1 = coprocessor << 8 ++ cmpne r1,#3<<8 ++ movge pc,r9 @ copro = 0 or >=3, return ++ ++ str r5,[r13,#60] @ save updated pc ++ b next_enter ++ ++/*---------------------------------------------------------------------------*/ ++ ++undefined: ++ ldr r4,=fp_undef ++ ldr pc,[r4] ++ ++/*---------------------------------------------------------------------------*/ ++ ++CPDT_1_enter: ++ and r5,r4,#0x000f0000 @ r5=base register number << 16 ++ ldr r6,[r13,r5,lsr#14] @ r6=base address ++ cmp r5,#0x000f0000 @ base register = pc ? ++ addeq r6,r6,#4 ++ and r7,r4,#0x000000ff @ r7=offset value ++ ++ tst r4,#0x00800000 @ up or down? ++ addne r7,r6,r7,lsl#2 ++ subeq r7,r6,r7,lsl#2 @ r6=base address +/- offset ++ tst r4,#0x01000000 @ preindexing ? ++ movne r6,r7 ++ tst r4,#0x00200000 @ write back ? ++ cmpne r5,#0x000f0000 @ base register = pc ? ++ strne r7,[r13,r5,lsr#14] ++ ++ and r0,r4,#0x00007000 @ r0=fp register number << 12 ++ add r0,r10,r0,lsr#8 @ r0=address of fp register ++ mov r1,#0 ++ tst r4,#0x00008000 ++ orrne r1,r1,#1 @ T0 ++ tst r4,#0x00400000 ++ orrne r1,r1,#2 @ T1 ++ tst r4,#0x00100000 ++ orrne r1,r1,#4 @ L/S ++ ++ add pc,pc,r1,lsl#2 ++ mov r0,r0 ++ b CPDT_store_single @ these functions get ++ b CPDT_store_double @ r0=address of fp register ++ b CPDT_store_extended @ r6=address of data ++ b undefined @ CPDT_store_decimal ++ b CPDT_load_single ++ b CPDT_load_double ++ b CPDT_load_extended ++ b undefined @ CPDT_load_decimal ++ ++/*---------------------------------------------------------------------------*/ ++ ++CPDT_M_enter: ++ and r5,r4,#0x000f0000 @ r5=base register number << 16 ++ ldr r6,[r13,r5,lsr#14] @ r6=base address ++ cmp r5,#0x000f0000 @ base register = pc ? ++ addeq r6,r6,#4 ++ and r7,r4,#0x000000ff @ r7=offset value ++ ++ tst r4,#0x00800000 @ up or down? ++ addne r7,r6,r7,lsl#2 ++ subeq r7,r6,r7,lsl#2 @ r7=base address +/- offset ++ tst r4,#0x01000000 @ preindexing ? ++ movne r6,r7 ++ tst r4,#0x00200000 @ write back ? ++ cmpne r5,#0x000f0000 @ base register = pc ? ++ strne r7,[r13,r5,lsr#14] ++ ++ and r0,r4,#0x00007000 @ r0=fp register number << 12 ++ and r1,r4,#0x00008000 ++ mov r1,r1,lsr#15 @ N0 ++ and r2,r4,#0x00400000 ++ orrs r1,r1,r2,lsr#21 @ N1 ++ addeq r1,r1,#4 @ r1=register count ++ ++ tst r4,#0x00100000 @ load/store ++ beq CPDT_sfm ++ b CPDT_lfm ++ ++/*---------------------------------------------------------------------------*/ ++ ++CPDO_CPRT_enter: ++ tst r4,#0x00000010 ++ bne CPRT_enter ++ ++ and r0,r4,#0x00007000 ++ add r0,r10,r0,lsr#8 @ r0=address of Fd ++ and r1,r4,#0x00070000 ++ add r1,r10,r1,lsr#12 @ r1=address of Fn ++ tst r4,#0x00000008 ++ bne CPDO_const ++ and r2,r4,#0x00000007 ++ add r2,r10,r2,lsl#4 @ r2=address of Fm ++ ++CPDO_constback: ++ and r3,r4,#0x00f00000 ++ tst r4,#0x00008000 ++ orrne r3,r3,#0x01000000 ++ ++ add pc,pc,r3,lsr#18 ++ mov r0,r0 ++ b CPDO_adf ++ b CPDO_muf ++ b CPDO_suf ++ b CPDO_rsf ++ b CPDO_dvf ++ b CPDO_rdf ++ b undefined ++ b undefined ++ b undefined @ CPDO_rmf ++ b CPDO_muf ++ b CPDO_dvf ++ b CPDO_rdf ++ b undefined ++ b undefined ++ b undefined ++ b undefined ++ b CPDO_mvf ++ b CPDO_mnf ++ b CPDO_abs ++ b CPDO_rnd ++ b CPDO_sqt ++ b undefined ++ b undefined ++ b undefined ++ b undefined ++ b undefined ++ b undefined ++ b undefined ++ b undefined ++ b undefined ++ b CPDO_rnd ++ b fastfpe_next ++ ++CPDO_const: ++ ldr r2,=fp_const ++ and r3,r4,#0x00000007 ++ add r2,r2,r3,lsl#4 ++ b CPDO_constback ++ ++/*---------------------------------------------------------------------------*/ ++ ++CPRT_enter: ++ and r0,r4,#0x0000f000 @ r0=Rd<<12 ++ and r1,r4,#0x00070000 ++ add r1,r10,r1,lsr#12 @ r1=address of Fn ++ tst r4,#0x00000008 ++ bne CPRT_const ++ and r2,r4,#0x00000007 ++ add r2,r10,r2,lsl#4 @ r2=address of Fm ++ ++CPRT_constback: ++ and r3,r4,#0x00f00000 ++ ++ add pc,pc,r3,lsr#18 ++ mov r0,r0 ++ b CPRT_flt ++ b CPRT_fix ++ b CPRT_wfs ++ b CPRT_rfs ++ b undefined ++ b undefined ++ b undefined ++ b undefined ++ b undefined ++ b CPRT_cmf ++ b undefined ++ b CPRT_cnf ++ b undefined ++ b CPRT_cmf ++ b undefined ++ b CPRT_cnf ++ ++CPRT_const: ++ ldr r2,=fp_const ++ and r3,r4,#0x00000007 ++ add r2,r2,r3,lsl#4 ++ b CPRT_constback ++ ++/*---------------------------------------------------------------------------*/ ++ ++ @ The fetch of the next instruction to emulate could fault ++ ++ .section .fixup,"ax" ++ .align ++__f1: ++ mov pc,r9 ++ .previous ++ .section __ex_table,"a" ++ .align 3 ++ .long __x1,__f1 ++ .previous ++ ++/*---------------------------------------------------------------------------*/ +diff -urN linux-2.4.26/arch/arm/fastfpe/module.c linux-2.4.26-vrs1/arch/arm/fastfpe/module.c +--- linux-2.4.26/arch/arm/fastfpe/module.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/fastfpe/module.c 2004-01-14 21:32:23.000000000 +0000 +@@ -0,0 +1,78 @@ ++/* ++ Fast Floating Point Emulator ++ (c) Peter Teichmann ++ ++ 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef MODULE ++#define kern_fp_enter fp_enter ++ ++extern char fpe_type[]; ++#endif ++ ++static void (*orig_fp_enter)(void); /* old kern_fp_enter value */ ++extern void (*kern_fp_enter)(void); /* current FP handler */ ++extern void fastfpe_enter(void); /* forward declarations */ ++ ++#ifdef MODULE ++/* ++ * Return 0 if we can be unloaded. This can only happen if ++ * kern_fp_enter is still pointing at fastfpe_enter ++ */ ++static int fpe_unload(void) ++{ ++ return (kern_fp_enter == fastfpe_enter) ? 0 : 1; ++} ++#endif ++ ++static int __init fpe_init(void) ++{ ++#ifdef MODULE ++ if (!mod_member_present(&__this_module, can_unload)) ++ return -EINVAL; ++ __this_module.can_unload = fpe_unload; ++#else ++ if (fpe_type[0] && strcmp(fpe_type, "fastfpe")) ++ return 0; ++#endif ++ ++ printk("Fast Floating Point Emulator V0.9 (c) Peter Teichmann.\n"); ++ ++ /* Save pointer to the old FP handler and then patch ourselves in */ ++ orig_fp_enter = kern_fp_enter; ++ kern_fp_enter = fastfpe_enter; ++ ++ return 0; ++} ++ ++static void __exit fpe_exit(void) ++{ ++ /* Restore the values we saved earlier. */ ++ kern_fp_enter = orig_fp_enter; ++} ++ ++module_init(fpe_init); ++module_exit(fpe_exit); ++ ++MODULE_AUTHOR("Peter Teichmann "); ++MODULE_DESCRIPTION("Fast floating point emulator with full precision"); +diff -urN linux-2.4.26/arch/arm/kernel/calls.S linux-2.4.26-vrs1/arch/arm/kernel/calls.S +--- linux-2.4.26/arch/arm/kernel/calls.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/kernel/calls.S 2004-03-04 21:01:56.000000000 +0000 +@@ -115,7 +115,7 @@ + .long SYMBOL_NAME(sys_ni_syscall) /* was sys_profil */ + .long SYMBOL_NAME(sys_statfs) + /* 100 */ .long SYMBOL_NAME(sys_fstatfs) +- .long SYMBOL_NAME(sys_ni_syscall) ++ .long SYMBOL_NAME(sys_ni_syscall) /* 101 was sys_ioperm */ + .long SYMBOL_NAME(sys_socketcall) + .long SYMBOL_NAME(sys_syslog) + .long SYMBOL_NAME(sys_setitimer) +@@ -126,7 +126,7 @@ + .long SYMBOL_NAME(sys_ni_syscall) /* was sys_uname */ + /* 110 */ .long SYMBOL_NAME(sys_ni_syscall) /* was sys_iopl */ + .long SYMBOL_NAME(sys_vhangup) +- .long SYMBOL_NAME(sys_ni_syscall) ++ .long SYMBOL_NAME(sys_ni_syscall) /* 112 was sys_idle */ + .long SYMBOL_NAME(sys_syscall) /* call a syscall */ + .long SYMBOL_NAME(sys_wait4) + /* 115 */ .long SYMBOL_NAME(sys_swapoff) +@@ -137,7 +137,7 @@ + /* 120 */ .long SYMBOL_NAME(sys_clone_wapper) + .long SYMBOL_NAME(sys_setdomainname) + .long SYMBOL_NAME(sys_newuname) +- .long SYMBOL_NAME(sys_ni_syscall) ++ .long SYMBOL_NAME(sys_ni_syscall) /* 123 was sys_modify_ldt */ + .long SYMBOL_NAME(sys_adjtimex) + /* 125 */ .long SYMBOL_NAME(sys_mprotect) + .long SYMBOL_NAME(sys_sigprocmask) +@@ -180,7 +180,7 @@ + .long SYMBOL_NAME(sys_arm_mremap) + .long SYMBOL_NAME(sys_setresuid16) + /* 165 */ .long SYMBOL_NAME(sys_getresuid16) +- .long SYMBOL_NAME(sys_ni_syscall) ++ .long SYMBOL_NAME(sys_ni_syscall) /* 166 was sys_vm86 */ + .long SYMBOL_NAME(sys_query_module) + .long SYMBOL_NAME(sys_poll) + .long SYMBOL_NAME(sys_nfsservctl) +diff -urN linux-2.4.26/arch/arm/kernel/dma-rpc.c linux-2.4.26-vrs1/arch/arm/kernel/dma-rpc.c +--- linux-2.4.26/arch/arm/kernel/dma-rpc.c 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/kernel/dma-rpc.c 2004-01-14 21:38:42.000000000 +0000 +@@ -26,19 +26,6 @@ + #include + #include + +-#if 0 +-typedef enum { +- dma_size_8 = 1, +- dma_size_16 = 2, +- dma_size_32 = 4, +- dma_size_128 = 16 +-} dma_size_t; +- +-typedef struct { +- dma_size_t transfersize; +-} dma_t; +-#endif +- + #define TRANSFER_SIZE 2 + + #define CURA (0) +@@ -48,10 +35,6 @@ + #define CR (IOMD_IO0CR - IOMD_IO0CURA) + #define ST (IOMD_IO0ST - IOMD_IO0CURA) + +-#define state_prog_a 0 +-#define state_wait_a 1 +-#define state_wait_b 2 +- + static void iomd_get_next_sg(struct scatterlist *sg, dma_t *dma) + { + unsigned long end, offset, flags = 0; +@@ -65,7 +48,7 @@ + if (end > PAGE_SIZE) + end = PAGE_SIZE; + +- if (offset + (int) TRANSFER_SIZE > end) ++ if (offset + TRANSFER_SIZE >= end) + flags |= DMA_END_L; + + sg->length = end - TRANSFER_SIZE; +@@ -103,27 +86,31 @@ + if (!(status & DMA_ST_INT)) + return; + +- if (status & DMA_ST_OFL && !dma->sg) +- break; +- +- iomd_get_next_sg(&dma->cur_sg, dma); ++ if ((dma->state ^ status) & DMA_ST_AB) ++ iomd_get_next_sg(&dma->cur_sg, dma); + + switch (status & (DMA_ST_OFL | DMA_ST_AB)) { + case DMA_ST_OFL: /* OIA */ + case DMA_ST_AB: /* .IB */ + iomd_writel(dma->cur_sg.dma_address, base + CURA); + iomd_writel(dma->cur_sg.length, base + ENDA); ++ dma->state = DMA_ST_AB; + break; + + case DMA_ST_OFL | DMA_ST_AB: /* OIB */ + case 0: /* .IA */ + iomd_writel(dma->cur_sg.dma_address, base + CURB); + iomd_writel(dma->cur_sg.length, base + ENDB); ++ dma->state = 0; + break; + } ++ ++ if (status & DMA_ST_OFL && ++ dma->cur_sg.length == (DMA_END_S|DMA_END_L)) ++ break; + } while (1); + +- iomd_writeb(0, base + CR); ++ dma->state = ~DMA_ST_AB; + disable_irq(irq); + } + +@@ -158,6 +145,7 @@ + } + + iomd_writeb(DMA_CR_C, dma_base + CR); ++ dma->state = DMA_ST_AB; + } + + if (dma->dma_mode == DMA_MODE_READ) +@@ -171,13 +159,11 @@ + { + unsigned long dma_base = dma->dma_base; + unsigned long flags; +- unsigned int ctrl; + + local_irq_save(flags); +- ctrl = iomd_readb(dma_base + CR); +- if (ctrl & DMA_CR_E) ++ if (dma->state != ~DMA_ST_AB) + disable_irq(dma->dma_irq); +- iomd_writeb(ctrl & ~DMA_CR_E, dma_base + CR); ++ iomd_writeb(0, dma_base + CR); + local_irq_restore(flags); + } + +diff -urN linux-2.4.26/arch/arm/kernel/entry-armv.S linux-2.4.26-vrs1/arch/arm/kernel/entry-armv.S +--- linux-2.4.26/arch/arm/kernel/entry-armv.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/kernel/entry-armv.S 2004-01-14 21:32:24.000000000 +0000 +@@ -677,12 +677,11 @@ + mrs r9, cpsr @ Enable interrupts if they were + tst r3, #I_BIT + biceq r9, r9, #I_BIT @ previously +- mov r0, r2 @ *** remove once everyones in sync + /* + * This routine must not corrupt r9 + */ + #ifdef MULTI_CPU +- ldr r4, .LCprocfns @ pass r0, r3 to ++ ldr r4, .LCprocfns @ pass r2, r3 to + mov lr, pc @ processor code + ldr pc, [r4] @ call processor specific code + #else +@@ -788,9 +787,8 @@ + stmdb r5, {sp, lr}^ + alignment_trap r7, r7, __temp_abt + zero_fp +- mov r0, r2 @ remove once everyones in sync + #ifdef MULTI_CPU +- ldr r4, .LCprocfns @ pass r0, r3 to ++ ldr r4, .LCprocfns @ pass r2, r3 to + mov lr, pc @ processor code + ldr pc, [r4] @ call processor specific code + #else +@@ -840,7 +838,8 @@ + adrsvc al, r9, ret_from_exception @ r9 = normal FP return + adrsvc al, lr, fpundefinstr @ lr = undefined instr return + +-call_fpe: get_current_task r10 ++call_fpe: enable_irq r10 ++ get_current_task r10 + mov r8, #1 + strb r8, [r10, #TSK_USED_MATH] @ set current->used_math + ldr r4, .LCfp +diff -urN linux-2.4.26/arch/arm/kernel/fiq.c linux-2.4.26-vrs1/arch/arm/kernel/fiq.c +--- linux-2.4.26/arch/arm/kernel/fiq.c 2003-06-13 15:51:29.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/kernel/fiq.c 2004-04-09 19:55:21.000000000 +0100 +@@ -122,23 +122,23 @@ + register unsigned long tmp, tmp2; + __asm__ volatile ( + #ifdef CONFIG_CPU_26 +- "mov %0, pc +- bic %1, %0, #0x3 +- orr %1, %1, %3 +- teqp %1, #0 @ select FIQ mode +- mov r0, r0 +- ldmia %2, {r8 - r14} +- teqp %0, #0 @ return to SVC mode +- mov r0, r0" ++ "mov %0, pc \n" ++ "bic %1, %0, #0x3 \n" ++ "orr %1, %1, %3 \n" ++ "teqp %1, #0 @ select FIQ mode \n" ++ "mov r0, r0 \n" ++ "ldmia %2, {r8 - r14} \n" ++ "teqp %0, #0 @ return to SVC mode \n" ++ "mov r0, r0 \n" + #endif + #ifdef CONFIG_CPU_32 +- "mrs %0, cpsr +- mov %1, %3 +- msr cpsr_c, %1 @ select FIQ mode +- mov r0, r0 +- ldmia %2, {r8 - r14} +- msr cpsr_c, %0 @ return to SVC mode +- mov r0, r0" ++ "mrs %0, cpsr \n" ++ "mov %1, %3 \n" ++ "msr cpsr_c, %1 @ select FIQ mode \n" ++ "mov r0, r0 \n" ++ "ldmia %2, {r8 - r14} \n" ++ "msr cpsr_c, %0 @ return to SVC mode \n" ++ "mov r0, r0 \n" + #endif + : "=&r" (tmp), "=&r" (tmp2) + : "r" (®s->ARM_r8), "I" (I_BIT | F_BIT | FIQ_MODE) +@@ -154,23 +154,23 @@ + register unsigned long tmp, tmp2; + __asm__ volatile ( + #ifdef CONFIG_CPU_26 +- "mov %0, pc +- bic %1, %0, #0x3 +- orr %1, %1, %3 +- teqp %1, #0 @ select FIQ mode +- mov r0, r0 +- stmia %2, {r8 - r14} +- teqp %0, #0 @ return to SVC mode +- mov r0, r0" ++ "mov %0, pc \n" ++ "bic %1, %0, #0x3 \n" ++ "orr %1, %1, %3 \n" ++ "teqp %1, #0 @ select FIQ mode \n" ++ "mov r0, r0 \n" ++ "stmia %2, {r8 - r14} \n" ++ "teqp %0, #0 @ return to SVC mode \n" ++ "mov r0, r0 \n" + #endif + #ifdef CONFIG_CPU_32 +- "mrs %0, cpsr +- mov %1, %3 +- msr cpsr_c, %1 @ select FIQ mode +- mov r0, r0 +- stmia %2, {r8 - r14} +- msr cpsr_c, %0 @ return to SVC mode +- mov r0, r0" ++ "mrs %0, cpsr \n" ++ "mov %1, %3 \n" ++ "msr cpsr_c, %1 @ select FIQ mode \n" ++ "mov r0, r0 \n" ++ "stmia %2, {r8 - r14} \n" ++ "msr cpsr_c, %0 @ return to SVC mode \n" ++ "mov r0, r0 \n" + #endif + : "=&r" (tmp), "=&r" (tmp2) + : "r" (®s->ARM_r8), "I" (I_BIT | F_BIT | FIQ_MODE) +diff -urN linux-2.4.26/arch/arm/kernel/head-armv.S linux-2.4.26-vrs1/arch/arm/kernel/head-armv.S +--- linux-2.4.26/arch/arm/kernel/head-armv.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/kernel/head-armv.S 2004-01-14 21:32:24.000000000 +0000 +@@ -1,7 +1,7 @@ + /* + * linux/arch/arm/kernel/head-armv.S + * +- * Copyright (C) 1994-1999 Russell King ++ * Copyright (C) 1994-2003 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as +@@ -163,10 +163,10 @@ + */ + .type __ret, %function + __ret: ldr lr, __switch_data +- mcr p15, 0, r0, c1, c0 +- mrc p15, 0, r0, c1, c0, 0 @ read it back. +- mov r0, r0 +- mov r0, r0 ++ mcr p15, 0, r0, c1, c0, 0 ++ mrc p15, 0, r3, c0, c0, 0 ++ mov r3, r3 ++ mov r3, r3 + mov pc, lr + + /* +@@ -214,6 +214,11 @@ + */ + __create_page_tables: + pgtbl r4, r5 @ page table address ++#if defined(CONFIG_CPU_DCACHE_DISABLE) ++ bic r8, r8, #0x00c @ clear B, C ++#elif defined(CONFIG_CPU_DCACHE_WRITETHROUGH) ++ bic r8, r8, #0x004 @ clear B ++#endif + + /* + * Clear the 16K level 1 swapper page table +diff -urN linux-2.4.26/arch/arm/kernel/irq.c linux-2.4.26-vrs1/arch/arm/kernel/irq.c +--- linux-2.4.26/arch/arm/kernel/irq.c 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/kernel/irq.c 2004-01-14 21:32:24.000000000 +0000 +@@ -549,7 +549,7 @@ + kfree(action); + goto out; + } +- printk(KERN_ERR "Trying to free free IRQ%d\n",irq); ++ printk(KERN_ERR "Trying to free IRQ%d\n",irq); + #ifdef CONFIG_DEBUG_ERRORS + __backtrace(); + #endif +diff -urN linux-2.4.26/arch/arm/kernel/ptrace.c linux-2.4.26-vrs1/arch/arm/kernel/ptrace.c +--- linux-2.4.26/arch/arm/kernel/ptrace.c 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/kernel/ptrace.c 2004-01-14 21:32:24.000000000 +0000 +@@ -725,11 +725,8 @@ + goto out_tsk; + } + ret = -ESRCH; +- if (!(child->ptrace & PT_PTRACED)) +- goto out_tsk; +- if (child->state != TASK_STOPPED && request != PTRACE_KILL) +- goto out_tsk; +- if (child->p_pptr != current) ++ ret = ptrace_check_attach(child, request == PTRACE_KILL); ++ if (ret) + goto out_tsk; + + ret = do_ptrace(request, child, addr, data); +diff -urN linux-2.4.26/arch/arm/kernel/semaphore.c linux-2.4.26-vrs1/arch/arm/kernel/semaphore.c +--- linux-2.4.26/arch/arm/kernel/semaphore.c 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/kernel/semaphore.c 2004-01-14 21:38:42.000000000 +0000 +@@ -193,7 +193,7 @@ + bl __down_interruptible \n\ + mov ip, r0 \n\ + ldmfd sp!, {r0 - r3, pc}^ \n\ +- ++ \n\ + .align 5 \n\ + .globl __down_trylock_failed \n\ + __down_trylock_failed: \n\ +diff -urN linux-2.4.26/arch/arm/kernel/signal.c linux-2.4.26-vrs1/arch/arm/kernel/signal.c +--- linux-2.4.26/arch/arm/kernel/signal.c 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/kernel/signal.c 2004-01-14 21:32:24.000000000 +0000 +@@ -641,10 +641,7 @@ + /* FALLTHRU */ + + default: +- sigaddset(¤t->pending.signal, signr); +- recalc_sigpending(current); +- current->flags |= PF_SIGNALED; +- do_exit(exit_code); ++ sig_exit(signr, exit_code, &info); + /* NOTREACHED */ + } + } +diff -urN linux-2.4.26/arch/arm/lib/Makefile linux-2.4.26-vrs1/arch/arm/lib/Makefile +--- linux-2.4.26/arch/arm/lib/Makefile 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/lib/Makefile 2004-01-14 21:32:24.000000000 +0000 +@@ -15,7 +15,7 @@ + strnlen_user.o strchr.o strrchr.o testchangebit.o \ + testclearbit.o testsetbit.o uaccess.o getuser.o \ + putuser.o ashldi3.o ashrdi3.o lshrdi3.o muldi3.o \ +- ucmpdi2.o udivdi3.o lib1funcs.o ++ ucmpdi2.o udivdi3.o lib1funcs.o div64.o + obj-m := + obj-n := + +diff -urN linux-2.4.26/arch/arm/lib/div64.S linux-2.4.26-vrs1/arch/arm/lib/div64.S +--- linux-2.4.26/arch/arm/lib/div64.S 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/lib/div64.S 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,59 @@ ++#include ++ ++#ifndef __ARMEB__ ++ql .req r0 @ quotient low ++qh .req r1 @ quotient high ++onl .req r0 @ original dividend low ++onh .req r1 @ original dividend high ++nl .req r4 @ dividend low ++nh .req r5 @ dividend high ++res .req r4 @ result ++#else ++ql .req r1 ++qh .req r0 ++onl .req r1 ++onh .req r0 ++nl .req r5 ++nh .req r4 ++res .req r5 ++#endif ++ ++dl .req r3 @ divisor low ++dh .req r2 @ divsor high ++ ++ ++ENTRY(do_div64) ++ stmfd sp!, {r4, r5, lr} ++ mov nl, onl ++ movs nh, onh @ if high bits are zero ++ movne lr, #33 ++ moveq lr, #1 @ only divide low bits ++ moveq nh, onl ++ ++ tst dh, #0x80000000 ++ bne 2f ++1: cmp nh, dh ++ bls 2f ++ add lr, lr, #1 ++ movs dh, dh, lsl #1 @ left justify disor ++ bpl 1b ++ ++2: movs nh, onh ++ moveq dl, dh ++ moveq dh, #0 ++ movne dl, #0 ++ mov ql, #0 ++ mov qh, #0 ++3: subs ip, nl, dl @ trial subtraction ++ sbcs ip, nh, dh ++ movcs nh, ip @ only update if successful ++ subcs nl, nl, dl @ (repeat the subtraction) ++ adcs ql, ql, ql @ C=1 if successful, shift into ++ adc qh, qh, qh @ quotient ++ movs dh, dh, lsr #1 @ shift base high part right ++ mov dl, dl, rrx @ shift base low part right ++ subs lr, lr, #1 ++ bne 3b ++ ++ mov r2, res ++ ldmfd sp!, {r4, r5, pc} +diff -urN linux-2.4.26/arch/arm/lib/putuser.S linux-2.4.26-vrs1/arch/arm/lib/putuser.S +--- linux-2.4.26/arch/arm/lib/putuser.S 2001-10-11 17:04:57.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/lib/putuser.S 2004-01-14 21:32:24.000000000 +0000 +@@ -30,11 +30,11 @@ + + .global __put_user_1 + __put_user_1: +- bic r2, sp, #0x1f00 +- bic r2, r2, #0x00ff +- ldr r2, [r2, #TSK_ADDR_LIMIT] +- sub r2, r2, #1 +- cmp r0, r2 ++ bic ip, sp, #0x1f00 ++ bic ip, ip, #0x00ff ++ ldr ip, [ip, #TSK_ADDR_LIMIT] ++ sub ip, ip, #1 ++ cmp r0, ip + 1: strlsbt r1, [r0] + movls r0, #0 + movls pc, lr +@@ -42,11 +42,11 @@ + + .global __put_user_2 + __put_user_2: +- bic r2, sp, #0x1f00 +- bic r2, r2, #0x00ff +- ldr r2, [r2, #TSK_ADDR_LIMIT] +- sub r2, r2, #2 +- cmp r0, r2 ++ bic ip, sp, #0x1f00 ++ bic ip, ip, #0x00ff ++ ldr ip, [ip, #TSK_ADDR_LIMIT] ++ sub ip, ip, #2 ++ cmp r0, ip + 2: strlsbt r1, [r0], #1 + movls r1, r1, lsr #8 + 3: strlsbt r1, [r0] +@@ -56,11 +56,11 @@ + + .global __put_user_4 + __put_user_4: +- bic r2, sp, #0x1f00 +- bic r2, r2, #0x00ff +- ldr r2, [r2, #TSK_ADDR_LIMIT] +- sub r2, r2, #4 +- cmp r0, r2 ++ bic ip, sp, #0x1f00 ++ bic ip, ip, #0x00ff ++ ldr ip, [ip, #TSK_ADDR_LIMIT] ++ sub ip, ip, #4 ++ cmp r0, ip + 4: strlst r1, [r0] + movls r0, #0 + movls pc, lr +diff -urN linux-2.4.26/arch/arm/mach-at91rm9200/Makefile linux-2.4.26-vrs1/arch/arm/mach-at91rm9200/Makefile +--- linux-2.4.26/arch/arm/mach-at91rm9200/Makefile 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-at91rm9200/Makefile 2004-04-10 12:21:08.000000000 +0100 +@@ -18,4 +18,8 @@ + + export-objs := + ++# LEDs support ++leds-$(CONFIG_ARCH_AT91RM9200DK) += dk-leds.o ++obj-$(CONFIG_LEDS) += $(leds-y) ++ + include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/arch/arm/mach-at91rm9200/dk-leds.c linux-2.4.26-vrs1/arch/arm/mach-at91rm9200/dk-leds.c +--- linux-2.4.26/arch/arm/mach-at91rm9200/dk-leds.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-at91rm9200/dk-leds.c 2004-04-10 12:21:08.000000000 +0100 +@@ -0,0 +1,97 @@ ++/* ++ * LED driver for the Atmel AT91RM9200 Development Kit. ++ * ++ * (c) SAN People (Pty) Ltd ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++*/ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++ ++static inline void at91_led_on(void) ++{ ++ AT91_SYS->PIOB_CODR = AT91C_PIO_PB2; ++} ++ ++static inline void at91_led_off(void) ++{ ++ AT91_SYS->PIOB_SODR = AT91C_PIO_PB2; ++} ++ ++static inline void at91_led_toggle(void) ++{ ++ unsigned long curr = AT91_SYS->PIOB_ODSR; ++ if (curr & AT91C_PIO_PB2) ++ AT91_SYS->PIOB_CODR = AT91C_PIO_PB2; ++ else ++ AT91_SYS->PIOB_SODR = AT91C_PIO_PB2; ++} ++ ++ ++/* ++ * Handle LED events. ++ */ ++static void at91rm9200dk_leds_event(led_event_t evt) ++{ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ switch(evt) { ++ case led_start: /* System startup */ ++ at91_led_on(); ++ break; ++ ++ case led_stop: /* System stop / suspend */ ++ at91_led_off(); ++ break; ++ ++#ifdef CONFIG_LEDS_TIMER ++ case led_timer: /* Every 50 timer ticks */ ++ at91_led_toggle(); ++ break; ++#endif ++ ++#ifdef CONFIG_LEDS_CPU ++ case led_idle_start: /* Entering idle state */ ++ at91_led_off(); ++ break; ++ ++ case led_idle_end: /* Exit idle state */ ++ at91_led_on(); ++ break; ++#endif ++ ++ default: ++ break; ++ } ++ ++ local_irq_restore(flags); ++} ++ ++ ++static int __init leds_init(void) ++{ ++ /* Enable PIO to access the LEDs */ ++ AT91_SYS->PIOB_PER = AT91C_PIO_PB2; ++ AT91_SYS->PIOB_OER = AT91C_PIO_PB2; ++ ++ leds_event = at91rm9200dk_leds_event; ++ ++ leds_event(led_start); ++ return 0; ++} ++ ++__initcall(leds_init); +diff -urN linux-2.4.26/arch/arm/mach-integrator/pci_v3.c linux-2.4.26-vrs1/arch/arm/mach-integrator/pci_v3.c +--- linux-2.4.26/arch/arm/mach-integrator/pci_v3.c 2003-06-13 15:51:29.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-integrator/pci_v3.c 2004-01-14 21:32:24.000000000 +0000 +@@ -21,7 +21,6 @@ + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #include +-#include + #include + #include + #include +@@ -32,6 +31,7 @@ + #include + + #include ++#include + #include + #include + #include +@@ -447,15 +447,16 @@ + #define SC_LBFADDR (IO_ADDRESS(INTEGRATOR_SC_BASE) + 0x20) + #define SC_LBFCODE (IO_ADDRESS(INTEGRATOR_SC_BASE) + 0x24) + +-static int v3_fault(unsigned long addr, struct pt_regs *regs) ++static int ++v3_pci_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) + { + unsigned long pc = instruction_pointer(regs); + unsigned long instr = *(unsigned long *)pc; + #if 0 + char buf[128]; + +- sprintf(buf, "V3 fault: address=0x%08lx, pc=0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n", +- addr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255, ++ sprintf(buf, "V3 fault: addr 0x%08lx, FSR 0x%03x, PC 0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n", ++ addr, fsr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255, + v3_readb(V3_LB_ISTAT)); + printk(KERN_DEBUG "%s", buf); + printascii(buf); +@@ -523,8 +524,6 @@ + #endif + } + +-extern int (*external_fault)(unsigned long addr, struct pt_regs *regs); +- + /* + * V3_LB_BASE? - local bus address + * V3_LB_MAP? - pci bus address +@@ -539,7 +538,10 @@ + /* + * Hook in our fault handler for PCI errors + */ +- external_fault = v3_fault; ++ hook_fault_code(4, v3_pci_fault, SIGBUS, "external abort on linefetch"); ++ hook_fault_code(6, v3_pci_fault, SIGBUS, "external abort on linefetch"); ++ hook_fault_code(8, v3_pci_fault, SIGBUS, "external abort on non-linefetch"); ++ hook_fault_code(10, v3_pci_fault, SIGBUS, "external abort on non-linefetch"); + + spin_lock_irqsave(&v3_lock, flags); + +@@ -629,7 +631,7 @@ + #if 0 + ret = request_irq(IRQ_LBUSTIMEOUT, lb_timeout, 0, "bus timeout", NULL); + if (ret) +- printk(KERN_ERR "PCI: unable to grab local bus timeout ". ++ printk(KERN_ERR "PCI: unable to grab local bus timeout " + "interrupt: %d\n", ret); + #endif + } +diff -urN linux-2.4.26/arch/arm/mach-sa1100/pm.c linux-2.4.26-vrs1/arch/arm/mach-sa1100/pm.c +--- linux-2.4.26/arch/arm/mach-sa1100/pm.c 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-sa1100/pm.c 2004-01-14 21:38:43.000000000 +0000 +@@ -21,6 +21,8 @@ + * + * 2002-05-27: Nicolas Pitre Killed sleep.h and the kmalloced save array. + * Storage is local on the stack now. ++ * 2003-06-25: Jeff Corrall ++ * Saved the GPIO levels for resume after sleep. + */ + #include + #include +@@ -70,13 +72,20 @@ + int pm_do_suspend(void) + { + unsigned long sleep_save[SLEEP_SAVE_SIZE]; ++ unsigned long sleep_save_gpsr; ++ unsigned long sleep_save_gpcr; ++ unsigned long delta; + + cli(); + + leds_event(led_stop); + + /* preserve current time */ +- RCNR = xtime.tv_sec; ++ delta = xtime.tv_sec - RCNR; ++ ++ /* save the current state of the GPIO output pins */ ++ sleep_save_gpsr = GPDR & GPLR; ++ sleep_save_gpcr = GPDR & ~GPLR; + + /* save vital registers */ + SAVE(OSCR); +@@ -121,6 +130,10 @@ + printk(KERN_DEBUG "*** made it back from resume\n"); + #endif + ++ /* restore GPIO output state before enabling the pins */ ++ GPSR = sleep_save_gpsr; ++ GPCR = sleep_save_gpcr; ++ + /* restore registers */ + RESTORE(GPDR); + RESTORE(GRER); +@@ -151,7 +164,7 @@ + RESTORE(ICMR); + + /* restore current time */ +- xtime.tv_sec = RCNR; ++ xtime.tv_sec = RCNR + delta; + + leds_event(led_start); + +diff -urN linux-2.4.26/arch/arm/mach-sa1100/sa1100_usb.h linux-2.4.26-vrs1/arch/arm/mach-sa1100/sa1100_usb.h +--- linux-2.4.26/arch/arm/mach-sa1100/sa1100_usb.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-sa1100/sa1100_usb.h 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,193 @@ ++/* ++ * sa1100_usb.h ++ * ++ * Public interface to the sa1100 USB core. For use by client modules ++ * like usb-eth and usb-char. ++ * ++ */ ++ ++#ifndef _SA1100_USB_H ++#define _SA1100_USB_H ++#include ++ ++typedef void (*usb_callback_t)(int flag, int size); ++ ++/* in usb_ctl.c (see also descriptor methods at bottom of file) */ ++ ++// Open the USB client for client and initialize data structures ++// to default values, but _do not_ start UDC. ++int sa1100_usb_open( const char * client_name ); ++ ++// Start UDC running ++int sa1100_usb_start( void ); ++ ++// Immediately stop udc, fire off completion routines w/-EINTR ++int sa1100_usb_stop( void ) ; ++ ++// Disconnect client from usb core ++int sa1100_usb_close( void ) ; ++ ++// set notify callback for when core reaches configured state ++// return previous pointer (if any) ++typedef void (*usb_notify_t)(void); ++usb_notify_t sa1100_set_configured_callback( usb_notify_t callback ); ++ ++/* in usb_send.c */ ++int sa1100_usb_xmitter_avail( void ); ++int sa1100_usb_send(char *buf, int len, usb_callback_t callback); ++void sa1100_usb_send_reset(void); ++ ++/* in usb_recev.c */ ++int sa1100_usb_recv(char *buf, int len, usb_callback_t callback); ++void sa1100_usb_recv_reset(void); ++ ++////////////////////////////////////////////////////////////////////////////// ++// Descriptor Management ++////////////////////////////////////////////////////////////////////////////// ++ ++#define DescriptorHeader \ ++ __u8 bLength; \ ++ __u8 bDescriptorType ++ ++ ++// --- Device Descriptor ------------------- ++ ++typedef struct { ++ DescriptorHeader; ++ __u16 bcdUSB; /* USB specification revision number in BCD */ ++ __u8 bDeviceClass; /* USB class for entire device */ ++ __u8 bDeviceSubClass; /* USB subclass information for entire device */ ++ __u8 bDeviceProtocol; /* USB protocol information for entire device */ ++ __u8 bMaxPacketSize0; /* Max packet size for endpoint zero */ ++ __u16 idVendor; /* USB vendor ID */ ++ __u16 idProduct; /* USB product ID */ ++ __u16 bcdDevice; /* vendor assigned device release number */ ++ __u8 iManufacturer; /* index of manufacturer string */ ++ __u8 iProduct; /* index of string that describes product */ ++ __u8 iSerialNumber; /* index of string containing device serial number */ ++ __u8 bNumConfigurations; /* number fo configurations */ ++} __attribute__ ((packed)) device_desc_t; ++ ++// --- Configuration Descriptor ------------ ++ ++typedef struct { ++ DescriptorHeader; ++ __u16 wTotalLength; /* total # of bytes returned in the cfg buf 4 this cfg */ ++ __u8 bNumInterfaces; /* number of interfaces in this cfg */ ++ __u8 bConfigurationValue; /* used to uniquely ID this cfg */ ++ __u8 iConfiguration; /* index of string describing configuration */ ++ __u8 bmAttributes; /* bitmap of attributes for ths cfg */ ++ __u8 MaxPower; /* power draw in 2ma units */ ++} __attribute__ ((packed)) config_desc_t; ++ ++// bmAttributes: ++enum { USB_CONFIG_REMOTEWAKE=0x20, USB_CONFIG_SELFPOWERED=0x40, ++ USB_CONFIG_BUSPOWERED=0x80 }; ++// MaxPower: ++#define USB_POWER( x) ((x)>>1) /* convert mA to descriptor units of A for MaxPower */ ++ ++// --- Interface Descriptor --------------- ++ ++typedef struct { ++ DescriptorHeader; ++ __u8 bInterfaceNumber; /* Index uniquely identfying this interface */ ++ __u8 bAlternateSetting; /* ids an alternate setting for this interface */ ++ __u8 bNumEndpoints; /* number of endpoints in this interface */ ++ __u8 bInterfaceClass; /* USB class info applying to this interface */ ++ __u8 bInterfaceSubClass; /* USB subclass info applying to this interface */ ++ __u8 bInterfaceProtocol; /* USB protocol info applying to this interface */ ++ __u8 iInterface; /* index of string describing interface */ ++} __attribute__ ((packed)) intf_desc_t; ++ ++// --- Endpoint Descriptor --------------- ++ ++typedef struct { ++ DescriptorHeader; ++ __u8 bEndpointAddress; /* 0..3 ep num, bit 7: 0 = 0ut 1= in */ ++ __u8 bmAttributes; /* 0..1 = 0: ctrl, 1: isoc, 2: bulk 3: intr */ ++ __u16 wMaxPacketSize; /* data payload size for this ep in this cfg */ ++ __u8 bInterval; /* polling interval for this ep in this cfg */ ++} __attribute__ ((packed)) ep_desc_t; ++ ++// bEndpointAddress: ++enum { USB_OUT= 0, USB_IN=1 }; ++#define USB_EP_ADDRESS(a,d) (((a)&0xf) | ((d) << 7)) ++// bmAttributes: ++enum { USB_EP_CNTRL=0, USB_EP_BULK=2, USB_EP_INT=3 }; ++ ++// --- String Descriptor ------------------- ++ ++typedef struct { ++ DescriptorHeader; ++ __u16 bString[1]; /* unicode string .. actaully 'n' __u16s */ ++} __attribute__ ((packed)) string_desc_t; ++ ++/*======================================================= ++ * Handy helpers when working with above ++ * ++ */ ++// these are x86-style 16 bit "words" ... ++#define make_word_c( w ) __constant_cpu_to_le16(w) ++#define make_word( w ) __cpu_to_le16(w) ++ ++// descriptor types ++enum { USB_DESC_DEVICE=1, USB_DESC_CONFIG=2, USB_DESC_STRING=3, ++ USB_DESC_INTERFACE=4, USB_DESC_ENDPOINT=5 }; ++ ++ ++/*======================================================= ++ * Default descriptor layout for SA-1100 and SA-1110 UDC ++ */ ++ ++/* "config descriptor buffer" - that is, one config, ++ ..one interface and 2 endpoints */ ++struct cdb { ++ config_desc_t cfg; ++ intf_desc_t intf; ++ ep_desc_t ep1; ++ ep_desc_t ep2; ++} __attribute__ ((packed)); ++ ++ ++/* all SA device descriptors */ ++typedef struct { ++ device_desc_t dev; /* device descriptor */ ++ struct cdb b; /* bundle of descriptors for this cfg */ ++} __attribute__ ((packed)) desc_t; ++ ++ ++/*======================================================= ++ * Descriptor API ++ */ ++ ++/* Get the address of the statically allocated desc_t structure ++ in the usb core driver. Clients can modify this between ++ the time they call sa1100_usb_open() and sa1100_usb_start() ++*/ ++desc_t * ++sa1100_usb_get_descriptor_ptr( void ); ++ ++ ++/* Set a pointer to the string descriptor at "index". The driver ++ ..has room for 8 string indicies internally. Index zero holds ++ ..a LANGID code and is set to US English by default. Inidices ++ ..1-7 are available for use in the config descriptors as client's ++ ..see fit. This pointer is assumed to be good as long as the ++ ..SA usb core is open (so statically allocate them). Returnes -EINVAL ++ ..if index out of range */ ++int sa1100_usb_set_string_descriptor( int index, string_desc_t * p ); ++ ++/* reverse of above */ ++string_desc_t * ++sa1100_usb_get_string_descriptor( int index ); ++ ++/* kmalloc() a string descriptor and convert "p" to unicode in it */ ++string_desc_t * ++sa1100_usb_kmalloc_string_descriptor( const char * p ); ++ ++ ++ ++ ++ ++ ++#endif /* _SA1100_USB_H */ +diff -urN linux-2.4.26/arch/arm/mach-sa1100/sa1111-ohci.c linux-2.4.26-vrs1/arch/arm/mach-sa1100/sa1111-ohci.c +--- linux-2.4.26/arch/arm/mach-sa1100/sa1111-ohci.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-sa1100/sa1111-ohci.c 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,140 @@ ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_USB_OHCI ++ ++/* ++ * The SA-1111 errata says that the DMA hardware needs to be exercised ++ * before the clocks are turned on to work properly. This code does ++ * a tiny dma transfer to prime to hardware. ++ * ++ * What DMA errata? I've checked October 1999 and February 2001, both ++ * of which do not mention such a bug, let alone any details of this ++ * work-around. ++ */ ++static void __init sa1111_dma_setup(void) ++{ ++ dma_addr_t dma_buf; ++ void * vbuf; ++ ++ /* DMA init & setup */ ++ ++ /* WARNING: The SA-1111 L3 function is used as part of this ++ * SA-1111 DMA errata workaround. ++ * ++ * N.B., When the L3 function is enabled, it uses GPIO_B<4:5> ++ * and takes precedence over the PS/2 mouse and GPIO_B ++ * functions. Refer to "Intel StrongARM SA-1111 Microprocessor ++ * Companion Chip, Sect 10.2" for details. So this "fix" may ++ * "break" support of either PS/2 mouse or GPIO_B if ++ * precautions are not taken to avoid collisions in ++ * configuration and use of these pins. AFAIK, no precautions ++ * are taken at this time. So it is likely that the action ++ * taken here may cause problems in PS/2 mouse and/or GPIO_B ++ * pin use elsewhere. ++ * ++ * But wait, there's more... What we're doing here is ++ * obviously altogether a bad idea. We're indiscrimanately bit ++ * flipping config for a few different functions here which ++ * are "owned" by other drivers. This needs to be handled ++ * better than it is being done here at this time. */ ++ ++ /* prime the dma engine with a tiny dma */ ++ SKPCR |= SKPCR_I2SCLKEN; ++ SKAUD |= SKPCR_L3CLKEN | SKPCR_SCLKEN; ++ ++ SACR0 |= 0x00003305; ++ SACR1 = 0x00000000; ++ ++ /* ++ * We need memory below 1MB. ++ * NOTE: consistent_alloc gives you some random virtual ++ * address as its return value, and the DMA address via ++ * the dma_addr_t pointer. ++ */ ++ vbuf = consistent_alloc(GFP_KERNEL | GFP_DMA, 4, &dma_buf); ++ ++ SADTSA = (unsigned long)dma_buf; ++ SADTCA = 4; ++ ++ SADTCS |= 0x00000011; ++ SKPCR |= SKPCR_DCLKEN; ++ ++ /* wait */ ++ udelay(100); ++ ++ /* clear reserved but, then disable SAC */ ++ SACR0 &= ~(0x00000002); ++ SACR0 &= ~(0x00000001); ++ ++ /* toggle bit clock direction */ ++ SACR0 |= 0x00000004; ++ SACR0 &= ~(0x00000004); ++ ++ SKAUD &= ~(SKPCR_L3CLKEN | SKPCR_SCLKEN); ++ ++ SKPCR &= ~SKPCR_I2SCLKEN; ++ ++ consistent_free(vbuf, 4, dma_buf); ++} ++ ++/* ++ * reset the SA-1111 usb controller and turn on it's clocks ++ */ ++int __init sa1111_ohci_hcd_init(void) ++{ ++ unsigned int usb_reset = 0; ++ ++ if (machine_is_xp860() || ++ machine_has_neponset() || ++ machine_is_pfs168() || ++ machine_is_badge4()) ++ usb_reset = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW; ++ ++ /* ++ * turn on USB clocks ++ */ ++ SKPCR |= SKPCR_UCLKEN; ++ udelay(100); ++ ++ /* ++ * Force USB reset ++ */ ++ USB_RESET = USB_RESET_FORCEIFRESET; ++ USB_RESET |= USB_RESET_FORCEHCRESET; ++ udelay(100); ++ ++ /* ++ * Take out of reset ++ */ ++ USB_RESET = 0; ++ ++ /* ++ * set power sense and control lines (this from the diags code) ++ */ ++ USB_RESET = usb_reset; ++ ++ /* ++ * Huh? This is a _read only_ register --rmk ++ */ ++ USB_STATUS = 0; ++ ++ udelay(10); ++ ++ /* ++ * compensate for dma bug ++ */ ++ sa1111_dma_setup(); ++ ++ return 0; ++} ++ ++void sa1111_ohci_hcd_cleanup(void) ++{ ++ /* turn the USB clock off */ ++ SKPCR &= ~SKPCR_UCLKEN; ++} ++ ++#endif /* CONFIG_USB_OHCI */ +diff -urN linux-2.4.26/arch/arm/mach-sa1100/usb-char.c linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb-char.c +--- linux-2.4.26/arch/arm/mach-sa1100/usb-char.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb-char.c 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,723 @@ ++/* ++ * (C) Copyright 2000-2001 Extenex Corporation ++ * ++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * usb-char.c ++ * ++ * Miscellaneous character device interface for SA1100 USB function ++ * driver. ++ * ++ * Background: ++ * The SA1100 function driver ported from the Compaq Itsy project ++ * has an interface, usb-eth.c, to feed network packets over the ++ * usb wire and into the Linux TCP/IP stack. ++ * ++ * This file replaces that one with a simple character device ++ * interface that allows unstructured "byte pipe" style reads and ++ * writes over the USB bulk endpoints by userspace programs. ++ * ++ * A new define, CONFIG_SA1100_USB_NETLINK, has been created that, ++ * when set, (the default) causes the ethernet interface to be used. ++ * When not set, this more pedestrian character interface is linked ++ * in instead. ++ * ++ * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. ++ * ++ * ward.willats@extenex.com ++ * ++ * To do: ++ * - Can't dma into ring buffer directly with pci_map/unmap usb_recv ++ * uses and get bytes out at the same time DMA is going on. Investigate: ++ * a) changing usb_recv to use alloc_consistent() at client request; or ++ * b) non-ring-buffer based data structures. In the meantime, I am using ++ * a bounce buffer. Simple, but wasteful. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "usb-char.h" ++#include "sa1100_usb.h" ++ ++ ++ ++////////////////////////////////////////////////////////////////////////////// ++// Driver Options ++////////////////////////////////////////////////////////////////////////////// ++ ++#define VERSION "0.4" ++ ++ ++#define VERBOSITY 1 ++ ++#if VERBOSITY ++# define PRINTK(x, a...) printk (x, ## a) ++#else ++# define PRINTK(x, a...) /**/ ++#endif ++ ++////////////////////////////////////////////////////////////////////////////// ++// Globals - Macros - Enums - Structures ++////////////////////////////////////////////////////////////////////////////// ++#ifndef MIN ++#define MIN( a, b ) ((a)<(b)?(a):(b)) ++#endif ++ ++typedef int bool; enum { false = 0, true = 1 }; ++ ++static const char pszMe[] = "usbchr: "; ++ ++static wait_queue_head_t wq_read; ++static wait_queue_head_t wq_write; ++static wait_queue_head_t wq_poll; ++ ++/* Serialze multiple writers onto the transmit hardware ++.. since we sleep the writer during transmit to stay in ++.. sync. (Multiple writers don't make much sense, but..) */ ++static DECLARE_MUTEX( xmit_sem ); ++ ++// size of usb DATA0/1 packets. 64 is standard maximum ++// for bulk transport, though most hosts seem to be able ++// to handle larger. ++#define TX_PACKET_SIZE 64 ++#define RX_PACKET_SIZE 64 ++#define RBUF_SIZE (4*PAGE_SIZE) ++ ++static struct wcirc_buf { ++ char *buf; ++ int in; ++ int out; ++} rx_ring = { NULL, 0, 0 }; ++ ++static struct { ++ unsigned long cnt_rx_complete; ++ unsigned long cnt_rx_errors; ++ unsigned long bytes_rx; ++ unsigned long cnt_tx_timeouts; ++ unsigned long cnt_tx_errors; ++ unsigned long bytes_tx; ++} charstats; ++ ++ ++static char * tx_buf = NULL; ++static char * packet_buffer = NULL; ++static int sending = 0; ++static int usb_ref_count = 0; ++static int last_tx_result = 0; ++static int last_rx_result = 0; ++static int last_tx_size = 0; ++static struct timer_list tx_timer; ++ ++////////////////////////////////////////////////////////////////////////////// ++// Prototypes ++////////////////////////////////////////////////////////////////////////////// ++static char * what_the_f( int e ); ++static void free_txrx_buffers( void ); ++static void twiddle_descriptors( void ); ++static void free_string_descriptors( void ) ; ++static int usbc_open( struct inode *pInode, struct file *pFile ); ++static void rx_done_callback_packet_buffer( int flag, int size ); ++ ++static void tx_timeout( unsigned long ); ++static void tx_done_callback( int flag, int size ); ++ ++static ssize_t usbc_read( struct file *, char *, size_t, loff_t * ); ++static ssize_t usbc_write( struct file *, const char *, size_t, loff_t * ); ++static unsigned int usbc_poll( struct file *pFile, poll_table * pWait ); ++static int usbc_ioctl( struct inode *pInode, struct file *pFile, ++ unsigned int nCmd, unsigned long argument ); ++static int usbc_close( struct inode *pInode, struct file *pFile ); ++ ++#ifdef CONFIG_SA1100_EXTENEX1 ++static void extenex_configured_notify_proc( void ); ++#endif ++////////////////////////////////////////////////////////////////////////////// ++// Private Helpers ++////////////////////////////////////////////////////////////////////////////// ++ ++static char * what_the_f( int e ) ++{ ++ char * p; ++ switch( e ) { ++ case 0: ++ p = "noErr"; ++ break; ++ case -ENODEV: ++ p = "ENODEV - usb not in config state"; ++ break; ++ case -EBUSY: ++ p = "EBUSY - another request on the hardware"; ++ break; ++ case -EAGAIN: ++ p = "EAGAIN"; ++ break; ++ case -EINTR: ++ p = "EINTR - interrupted\n"; ++ break; ++ case -EPIPE: ++ p = "EPIPE - zero length xfer\n"; ++ break; ++ default: ++ p = "????"; ++ break; ++ } ++ return p; ++} ++ ++static void free_txrx_buffers( void ) ++{ ++ if ( rx_ring.buf != NULL ) { ++ kfree( rx_ring.buf ); ++ rx_ring.buf = NULL; ++ } ++ if ( packet_buffer != NULL ) { ++ kfree( packet_buffer ); ++ packet_buffer = NULL; ++ } ++ if ( tx_buf != NULL ) { ++ kfree( tx_buf ); ++ tx_buf = NULL; ++ } ++} ++ ++/* twiddle_descriptors() ++ * It is between open() and start(). Setup descriptors. ++ */ ++static void twiddle_descriptors( void ) ++{ ++ desc_t * pDesc = sa1100_usb_get_descriptor_ptr(); ++ string_desc_t * pString; ++ ++ pDesc->b.ep1.wMaxPacketSize = make_word_c( RX_PACKET_SIZE ); ++ pDesc->b.ep1.bmAttributes = USB_EP_BULK; ++ pDesc->b.ep2.wMaxPacketSize = make_word_c( TX_PACKET_SIZE ); ++ pDesc->b.ep2.bmAttributes = USB_EP_BULK; ++ ++ if ( machine_is_extenex1() ) { ++#ifdef CONFIG_SA1100_EXTENEX1 ++ pDesc->dev.idVendor = make_word_c( 0xC9F ); ++ pDesc->dev.idProduct = 1; ++ pDesc->dev.bcdDevice = make_word_c( 0x0001 ); ++ pDesc->b.cfg.bmAttributes = USB_CONFIG_SELFPOWERED; ++ pDesc->b.cfg.MaxPower = 0; ++ ++ pString = sa1100_usb_kmalloc_string_descriptor( "Extenex" ); ++ if ( pString ) { ++ sa1100_usb_set_string_descriptor( 1, pString ); ++ pDesc->dev.iManufacturer = 1; ++ } ++ ++ pString = sa1100_usb_kmalloc_string_descriptor( "Handheld Theater" ); ++ if ( pString ) { ++ sa1100_usb_set_string_descriptor( 2, pString ); ++ pDesc->dev.iProduct = 2; ++ } ++ ++ pString = sa1100_usb_kmalloc_string_descriptor( "00000000" ); ++ if ( pString ) { ++ sa1100_usb_set_string_descriptor( 3, pString ); ++ pDesc->dev.iSerialNumber = 3; ++ } ++ ++ pString = sa1100_usb_kmalloc_string_descriptor( "HHT Bulk Transfer" ); ++ if ( pString ) { ++ sa1100_usb_set_string_descriptor( 4, pString ); ++ pDesc->b.intf.iInterface = 4; ++ } ++ sa1100_set_configured_callback( extenex_configured_notify_proc ); ++#endif ++ } ++} ++ ++static void free_string_descriptors( void ) ++{ ++ if ( machine_is_extenex1() ) { ++ string_desc_t * pString; ++ int i; ++ for( i = 1 ; i <= 4 ; i++ ) { ++ pString = sa1100_usb_get_string_descriptor( i ); ++ if ( pString ) ++ kfree( pString ); ++ } ++ } ++} ++ ++////////////////////////////////////////////////////////////////////////////// ++// ASYNCHRONOUS ++////////////////////////////////////////////////////////////////////////////// ++static void kick_start_rx( void ) ++{ ++ if ( usb_ref_count ) { ++ int total_space = CIRC_SPACE( rx_ring.in, rx_ring.out, RBUF_SIZE ); ++ if ( total_space >= RX_PACKET_SIZE ) { ++ sa1100_usb_recv( packet_buffer, ++ RX_PACKET_SIZE, ++ rx_done_callback_packet_buffer ++ ); ++ } ++ } ++} ++/* ++ * rx_done_callback_packet_buffer() ++ * We have completed a DMA xfer into the temp packet buffer. ++ * Move to ring. ++ * ++ * flag values: ++ * on init, -EAGAIN ++ * on reset, -EINTR ++ * on RPE, -EIO ++ * on short packet -EPIPE ++ */ ++static void ++rx_done_callback_packet_buffer( int flag, int size ) ++{ ++ charstats.cnt_rx_complete++; ++ ++ if ( flag == 0 || flag == -EPIPE ) { ++ size_t n; ++ ++ charstats.bytes_rx += size; ++ ++ n = CIRC_SPACE_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE ); ++ n = MIN( n, size ); ++ size -= n; ++ ++ memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer, n ); ++ rx_ring.in = (rx_ring.in + n) & (RBUF_SIZE-1); ++ memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer + n, size ); ++ rx_ring.in = (rx_ring.in + size) & (RBUF_SIZE-1); ++ ++ wake_up_interruptible( &wq_read ); ++ wake_up_interruptible( &wq_poll ); ++ ++ last_rx_result = 0; ++ ++ kick_start_rx(); ++ ++ } else if ( flag != -EAGAIN ) { ++ charstats.cnt_rx_errors++; ++ last_rx_result = flag; ++ wake_up_interruptible( &wq_read ); ++ wake_up_interruptible( &wq_poll ); ++ } ++ else /* init, start a read */ ++ kick_start_rx(); ++} ++ ++ ++static void tx_timeout( unsigned long unused ) ++{ ++ printk( "%stx timeout\n", pszMe ); ++ sa1100_usb_send_reset(); ++ charstats.cnt_tx_timeouts++; ++} ++ ++ ++// on init, -EAGAIN ++// on reset, -EINTR ++// on TPE, -EIO ++static void tx_done_callback( int flags, int size ) ++{ ++ if ( flags == 0 ) ++ charstats.bytes_tx += size; ++ else ++ charstats.cnt_tx_errors++; ++ last_tx_size = size; ++ last_tx_result = flags; ++ sending = 0; ++ wake_up_interruptible( &wq_write ); ++ wake_up_interruptible( &wq_poll ); ++} ++ ++ ++////////////////////////////////////////////////////////////////////////////// ++// Workers ++////////////////////////////////////////////////////////////////////////////// ++ ++static int usbc_open( struct inode *pInode, struct file *pFile ) ++{ ++ int retval = 0; ++ ++ PRINTK( KERN_DEBUG "%sopen()\n", pszMe ); ++ ++ /* start usb core */ ++ retval = sa1100_usb_open( "usb-char" ); ++ if ( retval ) return retval; ++ ++ /* allocate memory */ ++ if ( usb_ref_count == 0 ) { ++ tx_buf = (char*) kmalloc( TX_PACKET_SIZE, GFP_KERNEL | GFP_DMA ); ++ if ( tx_buf == NULL ) { ++ printk( "%sARGHH! COULD NOT ALLOCATE TX BUFFER\n", pszMe ); ++ goto malloc_fail; ++ } ++ rx_ring.buf = ++ (char*) kmalloc( RBUF_SIZE, GFP_KERNEL ); ++ ++ if ( rx_ring.buf == NULL ) { ++ printk( "%sARGHH! COULD NOT ALLOCATE RX BUFFER\n", pszMe ); ++ goto malloc_fail; ++ } ++ ++ packet_buffer = ++ (char*) kmalloc( RX_PACKET_SIZE, GFP_KERNEL | GFP_DMA ); ++ ++ if ( packet_buffer == NULL ) { ++ printk( "%sARGHH! COULD NOT ALLOCATE RX PACKET BUFFER\n", pszMe ); ++ goto malloc_fail; ++ } ++ rx_ring.in = rx_ring.out = 0; ++ memset( &charstats, 0, sizeof( charstats ) ); ++ sending = 0; ++ last_tx_result = 0; ++ last_tx_size = 0; ++ } ++ ++ /* modify default descriptors */ ++ twiddle_descriptors(); ++ ++ retval = sa1100_usb_start(); ++ if ( retval ) { ++ printk( "%sAGHH! Could not USB core\n", pszMe ); ++ free_txrx_buffers(); ++ return retval; ++ } ++ usb_ref_count++; /* must do _before_ kick_start() */ ++ MOD_INC_USE_COUNT; ++ kick_start_rx(); ++ return 0; ++ ++ malloc_fail: ++ free_txrx_buffers(); ++ return -ENOMEM; ++} ++ ++/* ++ * Read endpoint. Note that you can issue a read to an ++ * unconfigured endpoint. Eventually, the host may come along ++ * and configure underneath this module and data will appear. ++ */ ++static ssize_t usbc_read( struct file *pFile, char *pUserBuffer, ++ size_t stCount, loff_t *pPos ) ++{ ++ ssize_t retval; ++ int flags; ++ DECLARE_WAITQUEUE( wait, current ); ++ ++ PRINTK( KERN_DEBUG "%sread()\n", pszMe ); ++ ++ local_irq_save( flags ); ++ if ( last_rx_result == 0 ) { ++ local_irq_restore( flags ); ++ } else { /* an error happended and receiver is paused */ ++ local_irq_restore( flags ); ++ last_rx_result = 0; ++ kick_start_rx(); ++ } ++ ++ add_wait_queue( &wq_read, &wait ); ++ while( 1 ) { ++ ssize_t bytes_avail; ++ ssize_t bytes_to_end; ++ ++ set_current_state( TASK_INTERRUPTIBLE ); ++ ++ /* snap ring buf state */ ++ local_irq_save( flags ); ++ bytes_avail = CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE ); ++ bytes_to_end = CIRC_CNT_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE ); ++ local_irq_restore( flags ); ++ ++ if ( bytes_avail != 0 ) { ++ ssize_t bytes_to_move = MIN( stCount, bytes_avail ); ++ retval = 0; // will be bytes transfered ++ if ( bytes_to_move != 0 ) { ++ size_t n = MIN( bytes_to_end, bytes_to_move ); ++ if ( copy_to_user( pUserBuffer, ++ &rx_ring.buf[ rx_ring.out ], ++ n ) ) { ++ retval = -EFAULT; ++ break; ++ } ++ bytes_to_move -= n; ++ retval += n; ++ // might go 1 char off end, so wrap ++ rx_ring.out = ( rx_ring.out + n ) & (RBUF_SIZE-1); ++ if ( copy_to_user( pUserBuffer + n, ++ &rx_ring.buf[ rx_ring.out ], ++ bytes_to_move ) ++ ) { ++ retval = -EFAULT; ++ break; ++ } ++ rx_ring.out += bytes_to_move; // cannot wrap ++ retval += bytes_to_move; ++ kick_start_rx(); ++ } ++ break; ++ } ++ else if ( last_rx_result ) { ++ retval = last_rx_result; ++ break; ++ } ++ else if ( pFile->f_flags & O_NONBLOCK ) { // no data, can't sleep ++ retval = -EAGAIN; ++ break; ++ } ++ else if ( signal_pending( current ) ) { // no data, can sleep, but signal ++ retval = -ERESTARTSYS; ++ break; ++ } ++ schedule(); // no data, can sleep ++ } ++ set_current_state( TASK_RUNNING ); ++ remove_wait_queue( &wq_read, &wait ); ++ ++ if ( retval < 0 ) ++ printk( "%sread error %d - %s\n", pszMe, retval, what_the_f( retval ) ); ++ return retval; ++} ++ ++/* ++ * Write endpoint. This routine attempts to break the passed in buffer ++ * into usb DATA0/1 packet size chunks and send them to the host. ++ * (The lower-level driver tries to do this too, but easier for us ++ * to manage things here.) ++ * ++ * We are at the mercy of the host here, in that it must send an IN ++ * token to us to pull this data back, so hopefully some higher level ++ * protocol is expecting traffic to flow in that direction so the host ++ * is actually polling us. To guard against hangs, a 5 second timeout ++ * is used. ++ * ++ * This routine takes some care to only report bytes sent that have ++ * actually made it across the wire. Thus we try to stay in lockstep ++ * with the completion routine and only have one packet on the xmit ++ * hardware at a time. Multiple simultaneous writers will get ++ * "undefined" results. ++ * ++ */ ++static ssize_t usbc_write( struct file *pFile, const char * pUserBuffer, ++ size_t stCount, loff_t *pPos ) ++{ ++ ssize_t retval = 0; ++ ssize_t stSent = 0; ++ ++ DECLARE_WAITQUEUE( wait, current ); ++ ++ PRINTK( KERN_DEBUG "%swrite() %d bytes\n", pszMe, stCount ); ++ ++ down( &xmit_sem ); // only one thread onto the hardware at a time ++ ++ while( stCount != 0 && retval == 0 ) { ++ int nThisTime = MIN( TX_PACKET_SIZE, stCount ); ++ copy_from_user( tx_buf, pUserBuffer, nThisTime ); ++ sending = nThisTime; ++ retval = sa1100_usb_send( tx_buf, nThisTime, tx_done_callback ); ++ if ( retval < 0 ) { ++ char * p = what_the_f( retval ); ++ printk( "%sCould not queue xmission. rc=%d - %s\n", ++ pszMe, retval, p ); ++ sending = 0; ++ break; ++ } ++ /* now have something on the diving board */ ++ add_wait_queue( &wq_write, &wait ); ++ tx_timer.expires = jiffies + ( HZ * 5 ); ++ add_timer( &tx_timer ); ++ while( 1 ) { ++ set_current_state( TASK_INTERRUPTIBLE ); ++ if ( sending == 0 ) { /* it jumped into the pool */ ++ del_timer( &tx_timer ); ++ retval = last_tx_result; ++ if ( retval == 0 ) { ++ stSent += last_tx_size; ++ pUserBuffer += last_tx_size; ++ stCount -= last_tx_size; ++ } ++ else ++ printk( "%sxmission error rc=%d - %s\n", ++ pszMe, retval, what_the_f(retval) ); ++ break; ++ } ++ else if ( signal_pending( current ) ) { ++ del_timer( &tx_timer ); ++ printk( "%ssignal\n", pszMe ); ++ retval = -ERESTARTSYS; ++ break; ++ } ++ schedule(); ++ } ++ set_current_state( TASK_RUNNING ); ++ remove_wait_queue( &wq_write, &wait ); ++ } ++ ++ up( &xmit_sem ); ++ ++ if ( 0 == retval ) ++ retval = stSent; ++ return retval; ++} ++ ++static unsigned int usbc_poll( struct file *pFile, poll_table * pWait ) ++{ ++ unsigned int retval = 0; ++ ++ PRINTK( KERN_DEBUG "%poll()\n", pszMe ); ++ ++ poll_wait( pFile, &wq_poll, pWait ); ++ ++ if ( CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE ) ) ++ retval |= POLLIN | POLLRDNORM; ++ if ( sa1100_usb_xmitter_avail() ) ++ retval |= POLLOUT | POLLWRNORM; ++ return retval; ++} ++ ++static int usbc_ioctl( struct inode *pInode, struct file *pFile, ++ unsigned int nCmd, unsigned long argument ) ++{ ++ int retval = 0; ++ ++ switch( nCmd ) { ++ ++ case USBC_IOC_FLUSH_RECEIVER: ++ sa1100_usb_recv_reset(); ++ rx_ring.in = rx_ring.out = 0; ++ break; ++ ++ case USBC_IOC_FLUSH_TRANSMITTER: ++ sa1100_usb_send_reset(); ++ break; ++ ++ case USBC_IOC_FLUSH_ALL: ++ sa1100_usb_recv_reset(); ++ rx_ring.in = rx_ring.out = 0; ++ sa1100_usb_send_reset(); ++ break; ++ ++ default: ++ retval = -ENOIOCTLCMD; ++ break; ++ ++ } ++ return retval; ++} ++ ++ ++static int usbc_close( struct inode *pInode, struct file * pFile ) ++{ ++ PRINTK( KERN_DEBUG "%sclose()\n", pszMe ); ++ if ( --usb_ref_count == 0 ) { ++ down( &xmit_sem ); ++ sa1100_usb_stop(); ++ free_txrx_buffers(); ++ free_string_descriptors(); ++ del_timer( &tx_timer ); ++ sa1100_usb_close(); ++ up( &xmit_sem ); ++ } ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++#ifdef CONFIG_SA1100_EXTENEX1 ++#include "../../../drivers/char/ex_gpio.h" ++void extenex_configured_notify_proc( void ) ++{ ++ if ( exgpio_play_string( "440,1:698,1" ) == -EAGAIN ) ++ printk( "%sWanted to BEEP but ex_gpio not open\n", pszMe ); ++} ++#endif ++////////////////////////////////////////////////////////////////////////////// ++// Initialization ++////////////////////////////////////////////////////////////////////////////// ++ ++static struct file_operations usbc_fops = { ++ owner: THIS_MODULE, ++ open: usbc_open, ++ read: usbc_read, ++ write: usbc_write, ++ poll: usbc_poll, ++ ioctl: usbc_ioctl, ++ release: usbc_close, ++}; ++ ++static struct miscdevice usbc_misc_device = { ++ USBC_MINOR, "usb_char", &usbc_fops ++}; ++ ++/* ++ * usbc_init() ++ */ ++ ++int __init usbc_init( void ) ++{ ++ int rc; ++ ++#if !defined( CONFIG_ARCH_SA1100 ) ++ return -ENODEV; ++#endif ++ ++ if ( (rc = misc_register( &usbc_misc_device )) != 0 ) { ++ printk( KERN_WARNING "%sCould not register device 10, " ++ "%d. (%d)\n", pszMe, USBC_MINOR, rc ); ++ return -EBUSY; ++ } ++ ++ // initialize wait queues ++ init_waitqueue_head( &wq_read ); ++ init_waitqueue_head( &wq_write ); ++ init_waitqueue_head( &wq_poll ); ++ ++ // initialize tx timeout timer ++ init_timer( &tx_timer ); ++ tx_timer.function = tx_timeout; ++ ++ printk( KERN_INFO "USB Function Character Driver Interface" ++ " - %s, (C) 2001, Extenex Corp.\n", VERSION ++ ); ++ ++ return rc; ++} ++ ++void __exit usbc_exit( void ) ++{ ++} ++ ++EXPORT_NO_SYMBOLS; ++ ++module_init(usbc_init); ++module_exit(usbc_exit); ++ ++ ++ ++// end: usb-char.c ++ ++ ++ +diff -urN linux-2.4.26/arch/arm/mach-sa1100/usb-char.h linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb-char.h +--- linux-2.4.26/arch/arm/mach-sa1100/usb-char.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb-char.h 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,34 @@ ++/* ++ * Copyright (C) 2001 Extenex Corporation ++ * ++ * usb-char.h ++ * ++ * Character device emulation client for SA-1100 client usb core. ++ * ++ * ++ * ++ */ ++#ifndef _USB_CHAR_H ++#define _USB_CHAR_H ++ ++#define USBC_MAJOR 10 /* miscellaneous character device */ ++#define USBC_MINOR 240 /* in the "reserved for local use" range */ ++ ++#define USBC_MAGIC 0x8E ++ ++/* zap everything in receive ring buffer */ ++#define USBC_IOC_FLUSH_RECEIVER _IO( USBC_MAGIC, 0x01 ) ++ ++/* reset transmitter */ ++#define USBC_IOC_FLUSH_TRANSMITTER _IO( USBC_MAGIC, 0x02 ) ++ ++/* do both of above */ ++#define USBC_IOC_FLUSH_ALL _IO( USBC_MAGIC, 0x03 ) ++ ++ ++ ++ ++ ++ ++#endif /* _USB_CHAR_H */ ++ +diff -urN linux-2.4.26/arch/arm/mach-sa1100/usb-eth.c linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb-eth.c +--- linux-2.4.26/arch/arm/mach-sa1100/usb-eth.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb-eth.c 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,447 @@ ++ /* ++ * Ethernet driver for the SA1100 USB client function ++ * Copyright (c) 2001 by Nicolas Pitre ++ * ++ * This code was loosely inspired by the original initial ethernet test driver ++ * Copyright (c) Compaq Computer Corporation, 1999 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This is still work in progress... ++ * ++ * 19/02/2001 - Now we are compatible with generic usbnet driver. green@iXcelerator.com ++ * 09/03/2001 - Dropped 'framing' scheme, as it seems to cause a lot of problems with little benefit. ++ * Now, since we do not know what size of packet we are receiving ++ * last usb packet in sequence will always be less than max packet ++ * receive endpoint can accept. ++ * Now the only way to check correct start of frame is to compare ++ * MAC address. Also now we are stalling on each receive error. ++ * ++ * 15/03/2001 - Using buffer to get data from UDC. DMA needs to have 8 byte ++ * aligned buffer, but this breaks IP code (unaligned access). ++ * ++ * 01/04/2001 - stall endpoint operations appeared to be very unstable, so ++ * they are disabled now. ++ * ++ * 03/06/2001 - Readded "zerocopy" receive path (tunable). ++ * ++ */ ++ ++// Define DMA_NO_COPY if you want data to arrive directly into the ++// receive network buffers, instead of arriving into bounce buffer ++// and then get copied to network buffer. ++// This does not work correctly right now. ++#undef DMA_NO_COPY ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "sa1100_usb.h" ++ ++ ++#define ETHERNET_VENDOR_ID 0x49f ++#define ETHERNET_PRODUCT_ID 0x505A ++#define MAX_PACKET 32768 ++#define MIN(a, b) (((a) < (b)) ? (a) : (b)) ++ ++// Should be global, so that insmod can change these ++int usb_rsize=64; ++int usb_wsize=64; ++ ++static struct usbe_info_t { ++ struct net_device *dev; ++ u16 packet_id; ++ struct net_device_stats stats; ++} usbe_info; ++ ++static char usb_eth_name[16] = "usbf"; ++static struct net_device usb_eth_device; ++static struct sk_buff *cur_tx_skb, *next_tx_skb; ++static struct sk_buff *cur_rx_skb, *next_rx_skb; ++static volatile int terminating; ++#ifndef DMA_NO_COPY ++static char *dmabuf; // we need that, as dma expect it's buffers to be aligned on 8 bytes boundary ++#endif ++ ++static int usb_change_mtu (struct net_device *net, int new_mtu) ++{ ++ if (new_mtu <= sizeof (struct ethhdr) || new_mtu > MAX_PACKET) ++ return -EINVAL; ++ // no second zero-length packet read wanted after mtu-sized packets ++ if (((new_mtu + sizeof (struct ethhdr)) % usb_rsize) == 0) ++ return -EDOM; ++ ++ net->mtu = new_mtu; ++ return 0; ++} ++ ++static struct sk_buff * ++usb_new_recv_skb(void) ++{ ++ struct sk_buff *skb = alloc_skb( 2 + sizeof (struct ethhdr) + usb_eth_device.mtu,GFP_ATOMIC); ++ ++ if (skb) { ++ skb_reserve(skb, 2); ++ } ++ return skb; ++} ++ ++static u8 bcast_hwaddr[ETH_ALEN]={0xff,0xff,0xff,0xff,0xff,0xff}; ++static void ++usb_recv_callback(int flag, int size) ++{ ++ struct sk_buff *skb; ++ ++ if (terminating) ++ return; ++ ++ skb = cur_rx_skb; ++ ++ /* flag validation */ ++ if (flag == 0) { ++ if ( skb_tailroom (skb) < size ) { // hey! we are overloaded!!! ++ usbe_info.stats.rx_over_errors++; ++ goto error; ++ } ++#ifndef DMA_NO_COPY ++ memcpy(skb->tail,dmabuf,size); ++#endif ++ skb_put(skb, size); ++ } else { ++ if (flag == -EIO) { ++ usbe_info.stats.rx_errors++; ++ } ++ goto error; ++ } ++ ++ /* validate packet length */ ++ if (size == usb_rsize ) { ++ /* packet not complete yet */ ++ skb = NULL; ++ } ++ ++ /* ++ * At this point skb is non null if we have a complete packet. ++ * If so take a fresh skb right away and restart USB receive without ++ * further delays, then process the packet. Otherwise resume USB ++ * receive on the current skb and exit. ++ */ ++ ++ if (skb) ++ cur_rx_skb = next_rx_skb; ++#ifndef DMA_NO_COPY ++ sa1100_usb_recv(dmabuf, usb_rsize, ++ usb_recv_callback); ++#else ++ sa1100_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)), ++ usb_recv_callback); ++#endif ++ if (!skb) ++ return; ++ ++ next_rx_skb = usb_new_recv_skb(); ++ if (!next_rx_skb) { ++ /* ++ * We can't aford loosing buffer space... ++ * So we drop the current packet and recycle its skb. ++ */ ++ printk("%s: can't allocate new skb\n", __FUNCTION__); ++ usbe_info.stats.rx_dropped++; ++ skb_trim(skb, 0); ++ next_rx_skb = skb; ++ return; ++ } ++ if ( skb->len >= sizeof(struct ethhdr)) { ++ if (memcmp(skb->data,usb_eth_device.dev_addr,ETH_ALEN) && memcmp(skb->data,bcast_hwaddr,ETH_ALEN) ) { ++ // This frame is not for us. nor it is broadcast ++ usbe_info.stats.rx_frame_errors++; ++ kfree_skb(skb); ++ goto error; ++ } ++ } ++ ++ if (skb->len) { ++ int status; ++// FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ? ++ ++ skb->dev = &usb_eth_device; ++ skb->protocol = eth_type_trans (skb, &usb_eth_device); ++ usbe_info.stats.rx_packets++; ++ usbe_info.stats.rx_bytes += skb->len; ++ skb->ip_summed = CHECKSUM_NONE; ++ status = netif_rx (skb); ++ if (status != NET_RX_SUCCESS) ++ printk("netif_rx failed with code %d\n",status); ++ } else { ++error: ++ /* ++ * Error due to HW addr mismatch, or IO error. ++ * Recycle the current skb and reset USB reception. ++ */ ++ skb_trim(cur_rx_skb, 0); ++// if ( flag == -EINTR || flag == -EAGAIN ) // only if we are coming out of stall ++#ifndef DMA_NO_COPY ++ sa1100_usb_recv(dmabuf, usb_rsize, usb_recv_callback); ++#else ++ sa1100_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)), usb_recv_callback); ++#endif ++ } ++} ++ ++ ++static void ++usb_send_callback(int flag, int size) ++{ ++ struct net_device *dev = usbe_info.dev; ++ struct net_device_stats *stats; ++ struct sk_buff *skb=cur_tx_skb; ++ int ret; ++ ++ if (terminating) ++ return; ++ ++ stats = &usbe_info.stats; ++ switch (flag) { ++ case 0: ++ stats->tx_packets++; ++ stats->tx_bytes += size; ++ break; ++ case -EIO: ++ stats->tx_errors++; ++ break; ++ default: ++ stats->tx_dropped++; ++ break; ++ } ++ ++ cur_tx_skb = next_tx_skb; ++ next_tx_skb = NULL; ++ dev_kfree_skb_irq(skb); ++ if (!cur_tx_skb) ++ return; ++ ++ dev->trans_start = jiffies; ++ ret = sa1100_usb_send(cur_tx_skb->data, cur_tx_skb->len, usb_send_callback); ++ if (ret) { ++ /* If the USB core can't accept the packet, we drop it. */ ++ dev_kfree_skb_irq(cur_tx_skb); ++ cur_tx_skb = NULL; ++ usbe_info.stats.tx_carrier_errors++; ++ } ++ netif_wake_queue(dev); ++} ++ ++static int ++usb_eth_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ int ret; ++ unsigned long flags; ++ ++ if (next_tx_skb) { ++ printk("%s: called with next_tx_skb != NULL\n", __FUNCTION__); ++ return 1; ++ } ++ ++ if (skb_shared (skb)) { ++ struct sk_buff *skb2 = skb_unshare(skb, GFP_ATOMIC); ++ if (!skb2) { ++ usbe_info.stats.tx_dropped++; ++ dev_kfree_skb(skb); ++ return 1; ++ } ++ skb = skb2; ++ } ++ ++ if ((skb->len % usb_wsize) == 0) { ++ skb->len++; // other side will ignore this one, anyway. ++ } ++ ++ local_irq_save(flags); ++ if (cur_tx_skb) { ++ next_tx_skb = skb; ++ netif_stop_queue(dev); ++ } else { ++ cur_tx_skb = skb; ++ dev->trans_start = jiffies; ++ ret = sa1100_usb_send(skb->data, skb->len, usb_send_callback); ++ if (ret) { ++ /* If the USB core can't accept the packet, we drop it. */ ++ dev_kfree_skb(skb); ++ cur_tx_skb = NULL; ++ usbe_info.stats.tx_carrier_errors++; ++ } ++ } ++ local_irq_restore(flags); ++ return 0; ++} ++ ++static void ++usb_xmit_timeout(struct net_device *dev ) ++{ ++ sa1100_usb_send_reset(); ++ dev->trans_start = jiffies; ++ netif_wake_queue(dev); ++} ++ ++ ++static int ++usb_eth_open(struct net_device *dev) ++{ ++ terminating = 0; ++ cur_tx_skb = next_tx_skb = NULL; ++ cur_rx_skb = usb_new_recv_skb(); ++ next_rx_skb = usb_new_recv_skb(); ++ if (!cur_rx_skb || !next_rx_skb) { ++ printk("%s: can't allocate new skb\n", __FUNCTION__); ++ if (cur_rx_skb) ++ kfree_skb(cur_rx_skb); ++ if (next_rx_skb) ++ kfree_skb(next_rx_skb); ++ return -ENOMEM;; ++ } ++ ++ MOD_INC_USE_COUNT; ++#ifndef DMA_NO_COPY ++ sa1100_usb_recv(dmabuf, usb_rsize, usb_recv_callback); ++#else ++ sa1100_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)), ++ usb_recv_callback); ++#endif ++ return 0; ++} ++ ++static int ++usb_eth_release(struct net_device *dev) ++{ ++ terminating = 1; ++ sa1100_usb_send_reset(); ++ sa1100_usb_recv_reset(); ++ if (cur_tx_skb) ++ kfree_skb(cur_tx_skb); ++ if (next_tx_skb) ++ kfree_skb(next_tx_skb); ++ if (cur_rx_skb) ++ kfree_skb(cur_rx_skb); ++ if (next_rx_skb) ++ kfree_skb(next_rx_skb); ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++static struct net_device_stats * ++usb_eth_stats(struct net_device *dev) ++{ ++ struct usbe_info_t *priv = (struct usbe_info_t*) dev->priv; ++ struct net_device_stats *stats=NULL; ++ ++ if (priv) ++ stats = &priv->stats; ++ return stats; ++} ++ ++static int ++usb_eth_probe(struct net_device *dev) ++{ ++ u8 node_id [ETH_ALEN]; ++ ++ get_random_bytes (node_id, sizeof node_id); ++ node_id [0] &= 0xfe; // clear multicast bit ++ ++ /* ++ * Assign the hardware address of the board: ++ * generate it randomly, as there can be many such ++ * devices on the bus. ++ */ ++ memcpy (dev->dev_addr, node_id, sizeof node_id); ++ ++ dev->open = usb_eth_open; ++ dev->change_mtu = usb_change_mtu; ++ dev->stop = usb_eth_release; ++ dev->hard_start_xmit = usb_eth_xmit; ++ dev->get_stats = usb_eth_stats; ++ dev->watchdog_timeo = 1*HZ; ++ dev->tx_timeout = usb_xmit_timeout; ++ dev->priv = &usbe_info; ++ ++ usbe_info.dev = dev; ++ ++ /* clear the statistics */ ++ memset(&usbe_info.stats, 0, sizeof(struct net_device_stats)); ++ ++ ether_setup(dev); ++ dev->flags &= ~IFF_MULTICAST; ++ dev->flags &= ~IFF_BROADCAST; ++ //dev->flags |= IFF_NOARP; ++ ++ return 0; ++} ++ ++#ifdef MODULE ++MODULE_PARM(usb_rsize, "1i"); ++MODULE_PARM_DESC(usb_rsize, "number of bytes in packets from host to sa1100"); ++MODULE_PARM(usb_wsize, "1i"); ++MODULE_PARM_DESC(usb_wsize, "number of bytes in packets from sa1100 to host"); ++#endif ++ ++static int __init ++usb_eth_init(void) ++{ ++ int rc; ++ ++#ifndef DMA_NO_COPY ++ dmabuf = kmalloc( usb_rsize, GFP_KERNEL | GFP_DMA ); ++ if (!dmabuf) ++ return -ENOMEM; ++#endif ++ strncpy(usb_eth_device.name, usb_eth_name, IFNAMSIZ); ++ usb_eth_device.init = usb_eth_probe; ++ if (register_netdev(&usb_eth_device) != 0) ++ return -EIO; ++ ++ rc = sa1100_usb_open( "usb-eth" ); ++ if ( rc == 0 ) { ++ string_desc_t * pstr; ++ desc_t * pd = sa1100_usb_get_descriptor_ptr(); ++ ++ pd->b.ep1.wMaxPacketSize = make_word( usb_rsize ); ++ pd->b.ep2.wMaxPacketSize = make_word( usb_wsize ); ++ pd->dev.idVendor = ETHERNET_VENDOR_ID; ++ pd->dev.idProduct = ETHERNET_PRODUCT_ID; ++ pstr = sa1100_usb_kmalloc_string_descriptor( "SA1100 USB NIC" ); ++ if ( pstr ) { ++ sa1100_usb_set_string_descriptor( 1, pstr ); ++ pd->dev.iProduct = 1; ++ } ++ rc = sa1100_usb_start(); ++ } ++ return rc; ++} ++ ++module_init(usb_eth_init); ++ ++static void __exit ++usb_eth_cleanup(void) ++{ ++ string_desc_t * pstr; ++ sa1100_usb_stop(); ++ sa1100_usb_close(); ++ if ( (pstr = sa1100_usb_get_string_descriptor(1)) != NULL ) ++ kfree( pstr ); ++#ifndef DMA_NO_COPY ++ kfree(dmabuf); ++#endif ++ unregister_netdev(&usb_eth_device); ++} ++ ++module_exit(usb_eth_cleanup); +diff -urN linux-2.4.26/arch/arm/mach-sa1100/usb_ctl.c linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb_ctl.c +--- linux-2.4.26/arch/arm/mach-sa1100/usb_ctl.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb_ctl.c 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,774 @@ ++ /* ++ * Copyright (C) Compaq Computer Corporation, 1998, 1999 ++ * Copyright (C) Extenex Corporation, 2001 ++ * ++ * usb_ctl.c ++ * ++ * SA1100 USB controller core driver. ++ * ++ * This file provides interrupt routing and overall coordination ++ * of the three endpoints in usb_ep0, usb_receive (1), and usb_send (2). ++ * ++ * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sa1100_usb.h" ++#include "usb_ctl.h" ++ ++////////////////////////////////////////////////////////////////////////////// ++// Prototypes ++////////////////////////////////////////////////////////////////////////////// ++ ++int usbctl_next_state_on_event( int event ); ++static void udc_int_hndlr(int, void *, struct pt_regs *); ++static void initialize_descriptors( void ); ++static void soft_connect_hook( int enable ); ++static void udc_disable(void); ++static void udc_enable(void); ++ ++#if CONFIG_PROC_FS ++#define PROC_NODE_NAME "sausb" ++static int usbctl_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data); ++#endif ++ ++////////////////////////////////////////////////////////////////////////////// ++// Globals ++////////////////////////////////////////////////////////////////////////////// ++static const char pszMe[] = "usbctl: "; ++struct usb_info_t usbd_info; /* global to ep0, usb_recv, usb_send */ ++ ++/* device descriptors */ ++static desc_t desc; ++ ++#define MAX_STRING_DESC 8 ++static string_desc_t * string_desc_array[ MAX_STRING_DESC ]; ++static string_desc_t sd_zero; /* special sd_zero holds language codes */ ++ ++// called when configured ++static usb_notify_t configured_callback = NULL; ++ ++enum { kStateZombie = 0, kStateZombieSuspend = 1, ++ kStateDefault = 2, kStateDefaultSuspend = 3, ++ kStateAddr = 4, kStateAddrSuspend = 5, ++ kStateConfig = 6, kStateConfigSuspend = 7 ++}; ++ ++static int device_state_machine[8][6] = { ++// suspend reset resume adddr config deconfig ++/* zombie */ { kStateZombieSuspend, kStateDefault, kError, kError, kError, kError }, ++/* zom sus */ { kError, kStateDefault, kStateZombie, kError, kError, kError }, ++/* default */ { kStateDefaultSuspend, kError, kStateDefault, kStateAddr, kError, kError }, ++/* def sus */ { kError, kStateDefault, kStateDefault, kError, kError, kError }, ++/* addr */ { kStateAddrSuspend, kStateDefault, kError, kError, kStateConfig, kError }, ++/* addr sus */{ kError, kStateDefault, kStateAddr, kError, kError, kError }, ++/* config */ { kStateConfigSuspend, kStateDefault, kError, kError, kError, kStateAddr }, ++/* cfg sus */ { kError, kStateDefault, kStateConfig, kError, kError, kError } ++}; ++ ++/* "device state" is the usb device framework state, as opposed to the ++ "state machine state" which is whatever the driver needs and is much ++ more fine grained ++*/ ++static int sm_state_to_device_state[8] = ++// zombie zom suspend default default sus ++{ USB_STATE_POWERED, USB_STATE_SUSPENDED, USB_STATE_DEFAULT, USB_STATE_SUSPENDED, ++// addr addr sus config config sus ++ USB_STATE_ADDRESS, USB_STATE_SUSPENDED, USB_STATE_CONFIGURED, USB_STATE_SUSPENDED ++}; ++ ++static char * state_names[8] = ++{ "zombie", "zombie suspended", "default", "default suspended", ++ "address", "address suspended", "configured", "config suspended" ++}; ++ ++static char * event_names[6] = ++{ "suspend", "reset", "resume", ++ "address assigned", "configure", "de-configure" ++}; ++ ++static char * device_state_names[] = ++{ "not attached", "attached", "powered", "default", ++ "address", "configured", "suspended" }; ++ ++static int sm_state = kStateZombie; ++ ++////////////////////////////////////////////////////////////////////////////// ++// Async ++////////////////////////////////////////////////////////////////////////////// ++static void core_kicker(void); ++ ++static inline void enable_resume_mask_suspend( void ); ++static inline void enable_suspend_mask_resume(void); ++ ++static void ++udc_int_hndlr(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ __u32 status = Ser0UDCSR; ++ ++ /* ReSeT Interrupt Request - UDC has been reset */ ++ if ( status & UDCSR_RSTIR ) ++ { ++ if ( usbctl_next_state_on_event( kEvReset ) != kError ) ++ { ++ /* starting 20ms or so reset sequence now... */ ++ printk("%sResetting\n", pszMe); ++ ep0_reset(); // just set state to idle ++ ep1_reset(); // flush dma, clear false stall ++ ep2_reset(); // flush dma, clear false stall ++ } ++ // mask reset ints, they flood during sequence, enable ++ // suspend and resume ++ Ser0UDCCR |= UDCCR_REM; // mask reset ++ Ser0UDCCR &= ~(UDCCR_SUSIM | UDCCR_RESIM); // enable suspend and resume ++ UDC_flip( Ser0UDCSR, status ); // clear all pending sources ++ return; // <-- no reason to continue if resetting ++ } ++ // else we have done something other than reset, so be sure reset enabled ++ UDC_clear( Ser0UDCCR, UDCCR_REM ); ++ ++ /* RESume Interrupt Request */ ++ if ( status & UDCSR_RESIR ) ++ { ++ usbctl_next_state_on_event( kEvResume ); ++ core_kicker(); ++ enable_suspend_mask_resume(); ++ } ++ ++ /* SUSpend Interrupt Request */ ++ if ( status & UDCSR_SUSIR ) ++ { ++ usbctl_next_state_on_event( kEvSuspend ); ++ enable_resume_mask_suspend(); ++ } ++ ++ UDC_flip(Ser0UDCSR, status); // clear all pending sources ++ ++ if (status & UDCSR_EIR) ++ ep0_int_hndlr(); ++ ++ if (status & UDCSR_RIR) ++ ep1_int_hndlr(status); ++ ++ if (status & UDCSR_TIR) ++ ep2_int_hndlr(status); ++} ++ ++static inline void enable_resume_mask_suspend( void ) ++{ ++ int i = 0; ++ ++ while( 1 ) { ++ Ser0UDCCR |= UDCCR_SUSIM; // mask future suspend events ++ udelay( i ); ++ if ( (Ser0UDCCR & UDCCR_SUSIM) || (Ser0UDCSR & UDCSR_RSTIR) ) ++ break; ++ if ( ++i == 50 ) { ++ printk( "%senable_resume(): Could not set SUSIM %8.8X\n", ++ pszMe, Ser0UDCCR ); ++ break; ++ } ++ } ++ ++ i = 0; ++ while( 1 ) { ++ Ser0UDCCR &= ~UDCCR_RESIM; ++ udelay( i ); ++ if ( ( Ser0UDCCR & UDCCR_RESIM ) == 0 ++ || ++ (Ser0UDCSR & UDCSR_RSTIR) ++ ) ++ break; ++ if ( ++i == 50 ) { ++ printk( "%senable_resume(): Could not clear RESIM %8.8X\n", ++ pszMe, Ser0UDCCR ); ++ break; ++ } ++ } ++} ++ ++static inline void enable_suspend_mask_resume(void) ++{ ++ int i = 0; ++ while( 1 ) { ++ Ser0UDCCR |= UDCCR_RESIM; // mask future resume events ++ udelay( i ); ++ if ( Ser0UDCCR & UDCCR_RESIM || (Ser0UDCSR & UDCSR_RSTIR) ) ++ break; ++ if ( ++i == 50 ) { ++ printk( "%senable_suspend(): Could not set RESIM %8.8X\n", ++ pszMe, Ser0UDCCR ); ++ break; ++ } ++ } ++ i = 0; ++ while( 1 ) { ++ Ser0UDCCR &= ~UDCCR_SUSIM; ++ udelay( i ); ++ if ( ( Ser0UDCCR & UDCCR_SUSIM ) == 0 ++ || ++ (Ser0UDCSR & UDCSR_RSTIR) ++ ) ++ break; ++ if ( ++i == 50 ) { ++ printk( "%senable_suspend(): Could not clear SUSIM %8.8X\n", ++ pszMe, Ser0UDCCR ); ++ break; ++ } ++ } ++} ++ ++ ++////////////////////////////////////////////////////////////////////////////// ++// Public Interface ++////////////////////////////////////////////////////////////////////////////// ++ ++/* Open SA usb core on behalf of a client, but don't start running */ ++ ++int ++sa1100_usb_open( const char * client ) ++{ ++ if ( usbd_info.client_name != NULL ) ++ return -EBUSY; ++ ++ usbd_info.client_name = (char*) client; ++ memset(&usbd_info.stats, 0, sizeof(struct usb_stats_t)); ++ memset(string_desc_array, 0, sizeof(string_desc_array)); ++ ++ /* hack to start in zombie suspended state */ ++ sm_state = kStateZombieSuspend; ++ usbd_info.state = USB_STATE_SUSPENDED; ++ ++ /* create descriptors for enumeration */ ++ initialize_descriptors(); ++ ++ printk( "%sOpened for %s\n", pszMe, client ); ++ return 0; ++} ++ ++/* Start running. Must have called usb_open (above) first */ ++int ++sa1100_usb_start( void ) ++{ ++ if ( usbd_info.client_name == NULL ) { ++ printk( "%s%s - no client registered\n", ++ pszMe, __FUNCTION__ ); ++ return -EPERM; ++ } ++ ++ /* start UDC internal machinery running */ ++ udc_enable(); ++ udelay( 100 ); ++ ++ /* clear stall - receiver seems to start stalled? 19Jan01ww */ ++ /* also clear other stuff just to be thurough 22Feb01ww */ ++ UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC ); ++ UDC_clear(Ser0UDCCS2, UDCCS2_FST | UDCCS2_TPE | UDCCS2_TPC ); ++ ++ /* mask everything */ ++ Ser0UDCCR = 0xFC; ++ ++ /* flush DMA and fire through some -EAGAINs */ ++ ep1_init( usbd_info.dmach_rx ); ++ ep2_init( usbd_info.dmach_tx ); ++ ++ /* give endpoint notification we are starting */ ++ ep1_state_change_notify( USB_STATE_SUSPENDED ); ++ ep2_state_change_notify( USB_STATE_SUSPENDED ); ++ ++ /* enable any platform specific hardware */ ++ soft_connect_hook( 1 ); ++ ++ /* clear all top-level sources */ ++ Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR | ++ UDCSR_RIR | UDCSR_TIR | UDCSR_SUSIR ; ++ ++ /* EXERIMENT - a short line in the spec says toggling this ++ ..bit diddles the internal state machine in the udc to ++ ..expect a suspend */ ++ Ser0UDCCR |= UDCCR_RESIM; ++ /* END EXPERIMENT 10Feb01ww */ ++ ++ /* enable any platform specific hardware */ ++ soft_connect_hook( 1 ); ++ ++ /* enable interrupts. If you are unplugged you will ++ immediately get a suspend interrupt. If you are plugged ++ and have a soft connect-circuit, you will get a reset ++ If you are plugged without a soft-connect, I think you ++ also get suspend. In short, start with suspend masked ++ and everything else enabled */ ++ UDC_write( Ser0UDCCR, UDCCR_SUSIM ); ++ ++ printk( "%sStarted for %s\n", pszMe, usbd_info.client_name ); ++ return 0; ++} ++ ++/* Stop USB core from running */ ++int ++sa1100_usb_stop( void ) ++{ ++ if ( usbd_info.client_name == NULL ) { ++ printk( "%s%s - no client registered\n", ++ pszMe, __FUNCTION__ ); ++ return -EPERM; ++ } ++ /* mask everything */ ++ Ser0UDCCR = 0xFC; ++ ep1_reset(); ++ ep2_reset(); ++ udc_disable(); ++ printk( "%sStopped\n", pszMe ); ++ return 0; ++} ++ ++/* Tell SA core client is through using it */ ++int ++sa1100_usb_close( void ) ++{ ++ if ( usbd_info.client_name == NULL ) { ++ printk( "%s%s - no client registered\n", ++ pszMe, __FUNCTION__ ); ++ return -EPERM; ++ } ++ usbd_info.client_name = NULL; ++ printk( "%sClosed\n", pszMe ); ++ return 0; ++} ++ ++/* set a proc to be called when device is configured */ ++usb_notify_t sa1100_set_configured_callback( usb_notify_t func ) ++{ ++ usb_notify_t retval = configured_callback; ++ configured_callback = func; ++ return retval; ++} ++ ++/*==================================================== ++ * Descriptor Manipulation. ++ * Use these between open() and start() above to setup ++ * the descriptors for your device. ++ * ++ */ ++ ++/* get pointer to static default descriptor */ ++desc_t * ++sa1100_usb_get_descriptor_ptr( void ) { return &desc; } ++ ++/* optional: set a string descriptor */ ++int ++sa1100_usb_set_string_descriptor( int i, string_desc_t * p ) ++{ ++ int retval; ++ if ( i < MAX_STRING_DESC ) { ++ string_desc_array[i] = p; ++ retval = 0; ++ } else { ++ retval = -EINVAL; ++ } ++ return retval; ++} ++ ++/* optional: get a previously set string descriptor */ ++string_desc_t * ++sa1100_usb_get_string_descriptor( int i ) ++{ ++ return ( i < MAX_STRING_DESC ) ++ ? string_desc_array[i] ++ : NULL; ++} ++ ++ ++/* optional: kmalloc and unicode up a string descriptor */ ++string_desc_t * ++sa1100_usb_kmalloc_string_descriptor( const char * p ) ++{ ++ string_desc_t * pResult = NULL; ++ ++ if ( p ) { ++ int len = strlen( p ); ++ int uni_len = len * sizeof( __u16 ); ++ pResult = (string_desc_t*) kmalloc( uni_len + 2, GFP_KERNEL ); /* ugh! */ ++ if ( pResult != NULL ) { ++ int i; ++ pResult->bLength = uni_len + 2; ++ pResult->bDescriptorType = USB_DESC_STRING; ++ for( i = 0; i < len ; i++ ) { ++ pResult->bString[i] = make_word( (__u16) p[i] ); ++ } ++ } ++ } ++ return pResult; ++} ++ ++////////////////////////////////////////////////////////////////////////////// ++// Exports to rest of driver ++////////////////////////////////////////////////////////////////////////////// ++ ++/* called by the int handler here and the two endpoint files when interesting ++ .."events" happen */ ++ ++int ++usbctl_next_state_on_event( int event ) ++{ ++ int next_state = device_state_machine[ sm_state ][ event ]; ++ if ( next_state != kError ) ++ { ++ int next_device_state = sm_state_to_device_state[ next_state ]; ++ printk( "%s%s --> [%s] --> %s. Device in %s state.\n", ++ pszMe, state_names[ sm_state ], event_names[ event ], ++ state_names[ next_state ], device_state_names[ next_device_state ] ); ++ ++ sm_state = next_state; ++ if ( usbd_info.state != next_device_state ) ++ { ++ if ( configured_callback != NULL ++ && ++ next_device_state == USB_STATE_CONFIGURED ++ && ++ usbd_info.state != USB_STATE_SUSPENDED ++ ) { ++ configured_callback(); ++ } ++ usbd_info.state = next_device_state; ++ ep1_state_change_notify( next_device_state ); ++ ep2_state_change_notify( next_device_state ); ++ } ++ } ++#if 0 ++ else ++ printk( "%s%s --> [%s] --> ??? is an error.\n", ++ pszMe, state_names[ sm_state ], event_names[ event ] ); ++#endif ++ return next_state; ++} ++ ++////////////////////////////////////////////////////////////////////////////// ++// Private Helpers ++////////////////////////////////////////////////////////////////////////////// ++ ++/* setup default descriptors */ ++ ++static void ++initialize_descriptors(void) ++{ ++ desc.dev.bLength = sizeof( device_desc_t ); ++ desc.dev.bDescriptorType = USB_DESC_DEVICE; ++ desc.dev.bcdUSB = 0x100; /* 1.0 */ ++ desc.dev.bDeviceClass = 0xFF; /* vendor specific */ ++ desc.dev.bDeviceSubClass = 0; ++ desc.dev.bDeviceProtocol = 0; ++ desc.dev.bMaxPacketSize0 = 8; /* ep0 max fifo size */ ++ desc.dev.idVendor = 0; /* vendor ID undefined */ ++ desc.dev.idProduct = 0; /* product */ ++ desc.dev.bcdDevice = 0; /* vendor assigned device release num */ ++ desc.dev.iManufacturer = 0; /* index of manufacturer string */ ++ desc.dev.iProduct = 0; /* index of product description string */ ++ desc.dev.iSerialNumber = 0; /* index of string holding product s/n */ ++ desc.dev.bNumConfigurations = 1; ++ ++ desc.b.cfg.bLength = sizeof( config_desc_t ); ++ desc.b.cfg.bDescriptorType = USB_DESC_CONFIG; ++ desc.b.cfg.wTotalLength = make_word_c( sizeof(struct cdb) ); ++ desc.b.cfg.bNumInterfaces = 1; ++ desc.b.cfg.bConfigurationValue = 1; ++ desc.b.cfg.iConfiguration = 0; ++ desc.b.cfg.bmAttributes = USB_CONFIG_BUSPOWERED; ++ desc.b.cfg.MaxPower = USB_POWER( 500 ); ++ ++ desc.b.intf.bLength = sizeof( intf_desc_t ); ++ desc.b.intf.bDescriptorType = USB_DESC_INTERFACE; ++ desc.b.intf.bInterfaceNumber = 0; /* unique intf index*/ ++ desc.b.intf.bAlternateSetting = 0; ++ desc.b.intf.bNumEndpoints = 2; ++ desc.b.intf.bInterfaceClass = 0xFF; /* vendor specific */ ++ desc.b.intf.bInterfaceSubClass = 0; ++ desc.b.intf.bInterfaceProtocol = 0; ++ desc.b.intf.iInterface = 0; ++ ++ desc.b.ep1.bLength = sizeof( ep_desc_t ); ++ desc.b.ep1.bDescriptorType = USB_DESC_ENDPOINT; ++ desc.b.ep1.bEndpointAddress = USB_EP_ADDRESS( 1, USB_OUT ); ++ desc.b.ep1.bmAttributes = USB_EP_BULK; ++ desc.b.ep1.wMaxPacketSize = make_word_c( 64 ); ++ desc.b.ep1.bInterval = 0; ++ ++ desc.b.ep2.bLength = sizeof( ep_desc_t ); ++ desc.b.ep2.bDescriptorType = USB_DESC_ENDPOINT; ++ desc.b.ep2.bEndpointAddress = USB_EP_ADDRESS( 2, USB_IN ); ++ desc.b.ep2.bmAttributes = USB_EP_BULK; ++ desc.b.ep2.wMaxPacketSize = make_word_c( 64 ); ++ desc.b.ep2.bInterval = 0; ++ ++ /* set language */ ++ /* See: http://www.usb.org/developers/data/USB_LANGIDs.pdf */ ++ sd_zero.bDescriptorType = USB_DESC_STRING; ++ sd_zero.bLength = sizeof( string_desc_t ); ++ sd_zero.bString[0] = make_word_c( 0x409 ); /* American English */ ++ sa1100_usb_set_string_descriptor( 0, &sd_zero ); ++} ++ ++/* soft_connect_hook() ++ * Some devices have platform-specific circuitry to make USB ++ * not seem to be plugged in, even when it is. This allows ++ * software to control when a device 'appears' on the USB bus ++ * (after Linux has booted and this driver has loaded, for ++ * example). If you have such a circuit, control it here. ++ */ ++static void ++soft_connect_hook( int enable ) ++{ ++#ifdef CONFIG_SA1100_EXTENEX1 ++ if (machine_is_extenex1() ) { ++ if ( enable ) { ++ PPDR |= PPC_USB_SOFT_CON; ++ PPSR |= PPC_USB_SOFT_CON; ++ } else { ++ PPSR &= ~PPC_USB_SOFT_CON; ++ PPDR &= ~PPC_USB_SOFT_CON; ++ } ++ } ++#endif ++} ++ ++/* disable the UDC at the source */ ++static void ++udc_disable(void) ++{ ++ soft_connect_hook( 0 ); ++ UDC_set( Ser0UDCCR, UDCCR_UDD ); ++} ++ ++ ++/* enable the udc at the source */ ++static void ++udc_enable(void) ++{ ++ UDC_clear(Ser0UDCCR, UDCCR_UDD); ++} ++ ++// HACK DEBUG 3Mar01ww ++// Well, maybe not, it really seems to help! 08Mar01ww ++static void ++core_kicker( void ) ++{ ++ __u32 car = Ser0UDCAR; ++ __u32 imp = Ser0UDCIMP; ++ __u32 omp = Ser0UDCOMP; ++ ++ UDC_set( Ser0UDCCR, UDCCR_UDD ); ++ udelay( 300 ); ++ UDC_clear(Ser0UDCCR, UDCCR_UDD); ++ ++ Ser0UDCAR = car; ++ Ser0UDCIMP = imp; ++ Ser0UDCOMP = omp; ++} ++ ++////////////////////////////////////////////////////////////////////////////// ++// Proc Filesystem Support ++////////////////////////////////////////////////////////////////////////////// ++ ++#if CONFIG_PROC_FS ++ ++#define SAY( fmt, args... ) p += sprintf(p, fmt, ## args ) ++#define SAYV( num ) p += sprintf(p, num_fmt, "Value", num ) ++#define SAYC( label, yn ) p += sprintf(p, yn_fmt, label, yn ) ++#define SAYS( label, v ) p += sprintf(p, cnt_fmt, label, v ) ++ ++static int usbctl_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ const char * num_fmt = "%25.25s: %8.8lX\n"; ++ const char * cnt_fmt = "%25.25s: %lu\n"; ++ const char * yn_fmt = "%25.25s: %s\n"; ++ const char * yes = "YES"; ++ const char * no = "NO"; ++ unsigned long v; ++ char * p = page; ++ int len; ++ ++ SAY( "SA1100 USB Controller Core\n" ); ++ SAY( "USB state: %s (%s) %d\n", ++ device_state_names[ sm_state_to_device_state[ sm_state ] ], ++ state_names[ sm_state ], ++ sm_state ); ++ ++ SAYS( "ep0 bytes read", usbd_info.stats.ep0_bytes_read ); ++ SAYS( "ep0 bytes written", usbd_info.stats.ep0_bytes_written ); ++ SAYS( "ep0 FIFO read failures", usbd_info.stats.ep0_fifo_write_failures ); ++ SAYS( "ep0 FIFO write failures", usbd_info.stats.ep0_fifo_write_failures ); ++ ++ SAY( "\n" ); ++ ++ v = Ser0UDCAR; ++ SAY( "%25.25s: 0x%8.8lX - %ld\n", "Address Register", v, v ); ++ v = Ser0UDCIMP; ++ SAY( "%25.25s: %ld (%8.8lX)\n", "IN max packet size", v+1, v ); ++ v = Ser0UDCOMP; ++ SAY( "%25.25s: %ld (%8.8lX)\n", "OUT max packet size", v+1, v ); ++ ++ v = Ser0UDCCR; ++ SAY( "\nUDC Mask Register\n" ); ++ SAYV( v ); ++ SAYC( "UDC Active", ( v & UDCCR_UDA ) ? yes : no ); ++ SAYC( "Suspend interrupts masked", ( v & UDCCR_SUSIM ) ? yes : no ); ++ SAYC( "Resume interrupts masked", ( v & UDCCR_RESIM ) ? yes : no ); ++ SAYC( "Reset interrupts masked", ( v & UDCCR_REM ) ? yes : no ); ++ ++ v = Ser0UDCSR; ++ SAY( "\nUDC Interrupt Request Register\n" ); ++ SAYV( v ); ++ SAYC( "Reset pending", ( v & UDCSR_RSTIR ) ? yes : no ); ++ SAYC( "Suspend pending", ( v & UDCSR_SUSIR ) ? yes : no ); ++ SAYC( "Resume pending", ( v & UDCSR_RESIR ) ? yes : no ); ++ SAYC( "ep0 pending", ( v & UDCSR_EIR ) ? yes : no ); ++ SAYC( "receiver pending", ( v & UDCSR_RIR ) ? yes : no ); ++ SAYC( "tramsitter pending", ( v & UDCSR_TIR ) ? yes : no ); ++ ++#ifdef CONFIG_SA1100_EXTENEX1 ++ SAYC( "\nSoft connect", (PPSR & PPC_USB_SOFT_CON) ? "Visible" : "Hidden" ); ++#endif ++ ++#if 0 ++ v = Ser0UDCCS0; ++ SAY( "\nUDC Endpoint Zero Status Register\n" ); ++ SAYV( v ); ++ SAYC( "Out Packet Ready", ( v & UDCCS0_OPR ) ? yes : no ); ++ SAYC( "In Packet Ready", ( v & UDCCS0_IPR ) ? yes : no ); ++ SAYC( "Sent Stall", ( v & UDCCS0_SST ) ? yes : no ); ++ SAYC( "Force Stall", ( v & UDCCS0_FST ) ? yes : no ); ++ SAYC( "Data End", ( v & UDCCS0_DE ) ? yes : no ); ++ SAYC( "Data Setup End", ( v & UDCCS0_SE ) ? yes : no ); ++ SAYC( "Serviced (SO)", ( v & UDCCS0_SO ) ? yes : no ); ++ ++ v = Ser0UDCCS1; ++ SAY( "\nUDC Receiver Status Register\n" ); ++ SAYV( v ); ++ SAYC( "Receive Packet Complete", ( v & UDCCS1_RPC ) ? yes : no ); ++ SAYC( "Sent Stall", ( v & UDCCS1_SST ) ? yes : no ); ++ SAYC( "Force Stall", ( v & UDCCS1_FST ) ? yes : no ); ++ SAYC( "Receive Packet Error", ( v & UDCCS1_RPE ) ? yes : no ); ++ SAYC( "Receive FIFO not empty", ( v & UDCCS1_RNE ) ? yes : no ); ++ ++ v = Ser0UDCCS2; ++ SAY( "\nUDC Transmitter Status Register\n" ); ++ SAYV( v ); ++ SAYC( "FIFO has < 8 of 16 chars", ( v & UDCCS2_TFS ) ? yes : no ); ++ SAYC( "Transmit Packet Complete", ( v & UDCCS2_TPC ) ? yes : no ); ++ SAYC( "Transmit FIFO underrun", ( v & UDCCS2_TUR ) ? yes : no ); ++ SAYC( "Transmit Packet Error", ( v & UDCCS2_TPE ) ? yes : no ); ++ SAYC( "Sent Stall", ( v & UDCCS2_SST ) ? yes : no ); ++ SAYC( "Force Stall", ( v & UDCCS2_FST ) ? yes : no ); ++#endif ++ ++ len = ( p - page ) - off; ++ if ( len < 0 ) ++ len = 0; ++ *eof = ( len <=count ) ? 1 : 0; ++ *start = page + off; ++ return len; ++} ++ ++#endif /* CONFIG_PROC_FS */ ++ ++////////////////////////////////////////////////////////////////////////////// ++// Module Initialization and Shutdown ++////////////////////////////////////////////////////////////////////////////// ++/* ++ * usbctl_init() ++ * Module load time. Allocate dma and interrupt resources. Setup /proc fs ++ * entry. Leave UDC disabled. ++ */ ++int __init usbctl_init( void ) ++{ ++ int retval = 0; ++ ++ udc_disable(); ++ ++ memset( &usbd_info, 0, sizeof( usbd_info ) ); ++ ++#if CONFIG_PROC_FS ++ create_proc_read_entry ( PROC_NODE_NAME, 0, NULL, usbctl_read_proc, NULL); ++#endif ++ ++ /* setup rx dma */ ++ retval = sa1100_request_dma(&usbd_info.dmach_rx, "USB receive", DMA_Ser0UDCRd); ++ if (retval) { ++ printk("%sunable to register for rx dma rc=%d\n", pszMe, retval ); ++ goto err_rx_dma; ++ } ++ ++ /* setup tx dma */ ++ retval = sa1100_request_dma(&usbd_info.dmach_tx, "USB transmit", DMA_Ser0UDCWr); ++ if (retval) { ++ printk("%sunable to register for tx dma rc=%d\n",pszMe,retval); ++ goto err_tx_dma; ++ } ++ ++ /* now allocate the IRQ. */ ++ retval = request_irq(IRQ_Ser0UDC, udc_int_hndlr, SA_INTERRUPT, ++ "SA USB core", NULL); ++ if (retval) { ++ printk("%sCouldn't request USB irq rc=%d\n",pszMe, retval); ++ goto err_irq; ++ } ++ ++ printk( "SA1100 USB Controller Core Initialized\n"); ++ return 0; ++ ++err_irq: ++ sa1100_free_dma(usbd_info.dmach_tx); ++ usbd_info.dmach_tx = 0; ++err_tx_dma: ++ sa1100_free_dma(usbd_info.dmach_rx); ++ usbd_info.dmach_rx = 0; ++err_rx_dma: ++ return retval; ++} ++/* ++ * usbctl_exit() ++ * Release DMA and interrupt resources ++ */ ++void __exit usbctl_exit( void ) ++{ ++ printk("Unloading SA1100 USB Controller\n"); ++ ++ udc_disable(); ++ ++#if CONFIG_PROC_FS ++ remove_proc_entry ( PROC_NODE_NAME, NULL); ++#endif ++ ++ sa1100_free_dma(usbd_info.dmach_rx); ++ sa1100_free_dma(usbd_info.dmach_tx); ++ free_irq(IRQ_Ser0UDC, NULL); ++} ++ ++EXPORT_SYMBOL( sa1100_usb_open ); ++EXPORT_SYMBOL( sa1100_usb_start ); ++EXPORT_SYMBOL( sa1100_usb_stop ); ++EXPORT_SYMBOL( sa1100_usb_close ); ++ ++ ++EXPORT_SYMBOL( sa1100_usb_get_descriptor_ptr ); ++EXPORT_SYMBOL( sa1100_usb_set_string_descriptor ); ++EXPORT_SYMBOL( sa1100_usb_get_string_descriptor ); ++EXPORT_SYMBOL( sa1100_usb_kmalloc_string_descriptor ); ++ ++ ++module_init( usbctl_init ); ++module_exit( usbctl_exit ); +diff -urN linux-2.4.26/arch/arm/mach-sa1100/usb_ctl.h linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb_ctl.h +--- linux-2.4.26/arch/arm/mach-sa1100/usb_ctl.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb_ctl.h 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,123 @@ ++/* ++ * Copyright (C) Compaq Computer Corporation, 1998, 1999 ++ * Copyright (C) Extenex Corporation 2001 ++ * ++ * usb_ctl.h ++ * ++ * PRIVATE interface used to share info among components of the SA-1100 USB ++ * core: usb_ctl, usb_ep0, usb_recv and usb_send. Clients of the USB core ++ * should use sa1100_usb.h. ++ * ++ */ ++ ++#ifndef _USB_CTL_H ++#define _USB_CTL_H ++ ++#include /* dmach_t */ ++ ++ ++/* ++ * These states correspond to those in the USB specification v1.0 ++ * in chapter 8, Device Framework. ++ */ ++enum { USB_STATE_NOTATTACHED=0, USB_STATE_ATTACHED=1,USB_STATE_POWERED=2, ++ USB_STATE_DEFAULT=3, USB_STATE_ADDRESS=4, USB_STATE_CONFIGURED=5, ++ USB_STATE_SUSPENDED=6}; ++ ++struct usb_stats_t { ++ unsigned long ep0_fifo_write_failures; ++ unsigned long ep0_bytes_written; ++ unsigned long ep0_fifo_read_failures; ++ unsigned long ep0_bytes_read; ++}; ++ ++struct usb_info_t ++{ ++ char * client_name; ++ dmach_t dmach_tx, dmach_rx; ++ int state; ++ unsigned char address; ++ struct usb_stats_t stats; ++}; ++ ++/* in usb_ctl.c */ ++extern struct usb_info_t usbd_info; ++ ++/* ++ * Function Prototypes ++ */ ++enum { kError=-1, kEvSuspend=0, kEvReset=1, ++ kEvResume=2, kEvAddress=3, kEvConfig=4, kEvDeConfig=5 }; ++int usbctl_next_state_on_event( int event ); ++ ++/* endpoint zero */ ++void ep0_reset(void); ++void ep0_int_hndlr(void); ++ ++/* receiver */ ++void ep1_state_change_notify( int new_state ); ++int ep1_recv(void); ++int ep1_init(int chn); ++void ep1_int_hndlr(int status); ++void ep1_reset(void); ++void ep1_stall(void); ++ ++/* xmitter */ ++void ep2_state_change_notify( int new_state ); ++void ep2_reset(void); ++int ep2_init(int chn); ++void ep2_int_hndlr(int status); ++void ep2_stall(void); ++ ++#define UDC_write(reg, val) { \ ++ int i = 10000; \ ++ do { \ ++ (reg) = (val); \ ++ if (i-- <= 0) { \ ++ printk( "%s [%d]: write %#x to %p (%#x) failed\n", \ ++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ ++ break; \ ++ } \ ++ } while((reg) != (val)); \ ++} ++ ++#define UDC_set(reg, val) { \ ++ int i = 10000; \ ++ do { \ ++ (reg) |= (val); \ ++ if (i-- <= 0) { \ ++ printk( "%s [%d]: set %#x of %p (%#x) failed\n", \ ++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ ++ break; \ ++ } \ ++ } while(!((reg) & (val))); \ ++} ++ ++#define UDC_clear(reg, val) { \ ++ int i = 10000; \ ++ do { \ ++ (reg) &= ~(val); \ ++ if (i-- <= 0) { \ ++ printk( "%s [%d]: clear %#x of %p (%#x) failed\n", \ ++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ ++ break; \ ++ } \ ++ } while((reg) & (val)); \ ++} ++ ++#define UDC_flip(reg, val) { \ ++ int i = 10000; \ ++ (reg) = (val); \ ++ do { \ ++ (reg) = (val); \ ++ if (i-- <= 0) { \ ++ printk( "%s [%d]: flip %#x of %p (%#x) failed\n", \ ++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ ++ break; \ ++ } \ ++ } while(((reg) & (val))); \ ++} ++ ++ ++#define CHECK_ADDRESS { if ( Ser0UDCAR == 1 ) { printk("%s:%d I lost my address!!!\n",__FUNCTION__, __LINE__);}} ++#endif /* _USB_CTL_H */ +diff -urN linux-2.4.26/arch/arm/mach-sa1100/usb_ep0.c linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb_ep0.c +--- linux-2.4.26/arch/arm/mach-sa1100/usb_ep0.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb_ep0.c 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,911 @@ ++/* ++ * Copyright (C) Extenex Corporation 2001 ++ * Much folklore gleaned from original code: ++ * Copyright (C) Compaq Computer Corporation, 1998, 1999 ++ * ++ * usb_ep0.c - SA1100 USB controller driver. ++ * Endpoint zero management ++ * ++ * Please see: ++ * linux/Documentation/arm/SA1100/SA1100_USB ++ * for details. (Especially since Intel docs are full of ++ * errors about ep0 operation.) ward.willats@extenex.com. ++ * ++ * Intel also has a "Universal Serial Bus Client Device ++ * Validation for the StrongARM SA-1100 Microprocessor" ++ * document, which has flow charts and assembler test driver, ++ * but be careful, since it is just for validation and not ++ * a "real world" solution. ++ * ++ * A summary of three types of data-returning setups: ++ * ++ * 1. Setup request <= 8 bytes. That is, requests that can ++ * be fullfilled in one write to the FIFO. DE is set ++ * with IPR in queue_and_start_write(). (I don't know ++ * if there really are any of these!) ++ * ++ * 2. Setup requests > 8 bytes (requiring more than one ++ * IN to get back to the host), and we have at least ++ * as much or more data than the host requested. In ++ * this case we pump out everything we've got, and ++ * when the final interrupt comes in due to the UDC ++ * clearing the last IPR, we just set DE. ++ * ++ * 3. Setup requests > 8 bytes, but we don't have enough ++ * data to satisfy the request. In this case, we send ++ * everything we've got, and when the final interrupt ++ * comes in due to the UDC clearing the last IPR ++ * we write nothing to the FIFO and set both IPR and DE ++ * so the UDC sends an empty packet and forces the host ++ * to perform short packet retirement instead of stalling ++ * out. ++ * ++ */ ++ ++#include ++#include "sa1100_usb.h" /* public interface */ ++#include "usb_ctl.h" /* private stuff */ ++ ++ ++// 1 == lots of trace noise, 0 = only "important' stuff ++#define VERBOSITY 0 ++ ++enum { true = 1, false = 0 }; ++typedef int bool; ++#ifndef MIN ++#define MIN( a, b ) ((a)<(b)?(a):(b)) ++#endif ++ ++#if 1 && !defined( ASSERT ) ++# define ASSERT(expr) \ ++ if(!(expr)) { \ ++ printk( "Assertion failed! %s,%s,%s,line=%d\n",\ ++ #expr,__FILE__,__FUNCTION__,__LINE__); \ ++ } ++#else ++# define ASSERT(expr) ++#endif ++ ++#if VERBOSITY ++#define PRINTKD(fmt, args...) printk( fmt , ## args) ++#else ++#define PRINTKD(fmt, args...) ++#endif ++ ++/*================================================ ++ * USB Protocol Stuff ++ */ ++ ++/* Request Codes */ ++enum { GET_STATUS=0, CLEAR_FEATURE=1, SET_FEATURE=3, ++ SET_ADDRESS=5, GET_DESCRIPTOR=6, SET_DESCRIPTOR=7, ++ GET_CONFIGURATION=8, SET_CONFIGURATION=9, GET_INTERFACE=10, ++ SET_INTERFACE=11 }; ++ ++ ++/* USB Device Requests */ ++typedef struct ++{ ++ __u8 bmRequestType; ++ __u8 bRequest; ++ __u16 wValue; ++ __u16 wIndex; ++ __u16 wLength; ++} usb_dev_request_t __attribute__ ((packed)); ++ ++/*************************************************************************** ++Prototypes ++***************************************************************************/ ++/* "setup handlers" -- the main functions dispatched to by the ++ .. isr. These represent the major "modes" of endpoint 0 operaton */ ++static void sh_setup_begin(void); /* setup begin (idle) */ ++static void sh_write( void ); /* writing data */ ++static void sh_write_with_empty_packet( void ); /* empty packet at end of xfer*/ ++/* called before both sh_write routines above */ ++static void common_write_preamble( void ); ++ ++/* other subroutines */ ++static __u32 queue_and_start_write( void * p, int req, int act ); ++static void write_fifo( void ); ++static int read_fifo( usb_dev_request_t * p ); ++static void get_descriptor( usb_dev_request_t * pReq ); ++ ++/* some voodo helpers 01Mar01ww */ ++static void set_cs_bits( __u32 set_bits ); ++static void set_de( void ); ++static void set_ipr( void ); ++static void set_ipr_and_de( void ); ++static bool clear_opr( void ); ++ ++/*************************************************************************** ++Inline Helpers ++***************************************************************************/ ++ ++/* Data extraction from usb_request_t fields */ ++enum { kTargetDevice=0, kTargetInterface=1, kTargetEndpoint=2 }; ++static inline int request_target( __u8 b ) { return (int) ( b & 0x0F); } ++ ++static inline int windex_to_ep_num( __u16 w ) { return (int) ( w & 0x000F); } ++inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); } ++ ++/* following is hook for self-powered flag in GET_STATUS. Some devices ++ .. might like to override and return real info */ ++static inline bool self_powered_hook( void ) { return true; } ++ ++/* print string descriptor */ ++static inline void psdesc( string_desc_t * p ) ++{ ++ int i; ++ int nchars = ( p->bLength - 2 ) / sizeof( __u16 ); ++ printk( "'" ); ++ for( i = 0 ; i < nchars ; i++ ) { ++ printk( "%c", (char) p->bString[i] ); ++ } ++ printk( "'\n" ); ++} ++ ++ ++#if VERBOSITY ++/* "pcs" == "print control status" */ ++static inline void pcs( void ) ++{ ++ __u32 foo = Ser0UDCCS0; ++ printk( "%8.8X: %s %s %s %s\n", ++ foo, ++ foo & UDCCS0_SE ? "SE" : "", ++ foo & UDCCS0_OPR ? "OPR" : "", ++ foo & UDCCS0_IPR ? "IPR" : "", ++ foo & UDCCS0_SST ? "SST" : "" ++ ); ++} ++static inline void preq( usb_dev_request_t * pReq ) ++{ ++ static char * tnames[] = { "dev", "intf", "ep", "oth" }; ++ static char * rnames[] = { "std", "class", "vendor", "???" }; ++ char * psz; ++ switch( pReq->bRequest ) { ++ case GET_STATUS: psz = "get stat"; break; ++ case CLEAR_FEATURE: psz = "clr feat"; break; ++ case SET_FEATURE: psz = "set feat"; break; ++ case SET_ADDRESS: psz = "set addr"; break; ++ case GET_DESCRIPTOR: psz = "get desc"; break; ++ case SET_DESCRIPTOR: psz = "set desc"; break; ++ case GET_CONFIGURATION: psz = "get cfg"; break; ++ case SET_CONFIGURATION: psz = "set cfg"; break; ++ case GET_INTERFACE: psz = "get intf"; break; ++ case SET_INTERFACE: psz = "set intf"; break; ++ default: psz = "unknown"; break; ++ } ++ printk( "- [%s: %s req to %s. dir=%s]\n", psz, ++ rnames[ (pReq->bmRequestType >> 5) & 3 ], ++ tnames[ pReq->bmRequestType & 3 ], ++ ( pReq->bmRequestType & 0x80 ) ? "in" : "out" ); ++} ++ ++#else ++static inline void pcs( void ){} ++static inline void preq( void ){} ++#endif ++ ++/*************************************************************************** ++Globals ++***************************************************************************/ ++static const char pszMe[] = "usbep0: "; ++ ++/* pointer to current setup handler */ ++static void (*current_handler)(void) = sh_setup_begin; ++ ++/* global write struct to keep write ++ ..state around across interrupts */ ++static struct { ++ unsigned char *p; ++ int bytes_left; ++} wr; ++ ++/*************************************************************************** ++Public Interface ++***************************************************************************/ ++ ++/* reset received from HUB (or controller just went nuts and reset by itself!) ++ so udc core has been reset, track this state here */ ++void ++ep0_reset(void) ++{ ++ /* reset state machine */ ++ current_handler = sh_setup_begin; ++ wr.p = NULL; ++ wr.bytes_left = 0; ++ usbd_info.address=0; ++} ++ ++/* handle interrupt for endpoint zero */ ++void ++ep0_int_hndlr( void ) ++{ ++ PRINTKD( "/\\(%d)\n", Ser0UDCAR ); ++ pcs(); ++ ++ /* if not in setup begin, we are returning data. ++ execute a common preamble to both write handlers ++ */ ++ if ( current_handler != sh_setup_begin ) ++ common_write_preamble(); ++ ++ (*current_handler)(); ++ ++ PRINTKD( "---\n" ); ++ pcs(); ++ PRINTKD( "\\/\n" ); ++} ++ ++/*************************************************************************** ++Setup Handlers ++***************************************************************************/ ++/* ++ * sh_setup_begin() ++ * This setup handler is the "idle" state of endpoint zero. It looks for OPR ++ * (OUT packet ready) to see if a setup request has been been received from the ++ * host. Requests without a return data phase are immediately handled. Otherwise, ++ * in the case of GET_XXXX the handler may be set to one of the sh_write_xxxx ++ * data pumpers if more than 8 bytes need to get back to the host. ++ * ++ */ ++static void ++sh_setup_begin( void ) ++{ ++ unsigned char status_buf[2]; /* returned in GET_STATUS */ ++ usb_dev_request_t req; ++ int request_type; ++ int n; ++ __u32 cs_bits; ++ __u32 address; ++ __u32 cs_reg_in = Ser0UDCCS0; ++ ++ if (cs_reg_in & UDCCS0_SST) { ++ PRINTKD( "%ssetup begin: sent stall. Continuing\n", pszMe ); ++ set_cs_bits( UDCCS0_SST ); ++ } ++ ++ if ( cs_reg_in & UDCCS0_SE ) { ++ PRINTKD( "%ssetup begin: Early term of setup. Continuing\n", pszMe ); ++ set_cs_bits( UDCCS0_SSE ); /* clear setup end */ ++ } ++ ++ /* Be sure out packet ready, otherwise something is wrong */ ++ if ( (cs_reg_in & UDCCS0_OPR) == 0 ) { ++ /* we can get here early...if so, we'll int again in a moment */ ++ PRINTKD( "%ssetup begin: no OUT packet available. Exiting\n", pszMe ); ++ goto sh_sb_end; ++ } ++ ++ /* read the setup request */ ++ n = read_fifo( &req ); ++ if ( n != sizeof( req ) ) { ++ printk( "%ssetup begin: fifo READ ERROR wanted %d bytes got %d. " ++ " Stalling out...\n", ++ pszMe, sizeof( req ), n ); ++ /* force stall, serviced out */ ++ set_cs_bits( UDCCS0_FST | UDCCS0_SO ); ++ goto sh_sb_end; ++ } ++ ++ /* Is it a standard request? (not vendor or class request) */ ++ request_type = type_code_from_request( req.bmRequestType ); ++ if ( request_type != 0 ) { ++ printk( "%ssetup begin: unsupported bmRequestType: %d ignored\n", ++ pszMe, request_type ); ++ set_cs_bits( UDCCS0_DE | UDCCS0_SO ); ++ goto sh_sb_end; ++ } ++ ++#if VERBOSITY ++ { ++ unsigned char * pdb = (unsigned char *) &req; ++ PRINTKD( "%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X ", ++ pdb[0], pdb[1], pdb[2], pdb[3], pdb[4], pdb[5], pdb[6], pdb[7] ++ ); ++ preq( &req ); ++ } ++#endif ++ ++ /* Handle it */ ++ switch( req.bRequest ) { ++ ++ /* This first bunch have no data phase */ ++ ++ case SET_ADDRESS: ++ address = (__u32) (req.wValue & 0x7F); ++ /* when SO and DE sent, UDC will enter status phase and ack, ++ ..propagating new address to udc core. Next control transfer ++ ..will be on the new address. You can't see the change in a ++ ..read back of CAR until then. (about 250us later, on my box). ++ ..The original Intel driver sets S0 and DE and code to check ++ ..that address has propagated here. I tried this, but it ++ ..would only work sometimes! The rest of the time it would ++ ..never propagate and we'd spin forever. So now I just set ++ ..it and pray... ++ */ ++ Ser0UDCAR = address; ++ usbd_info.address = address; ++ usbctl_next_state_on_event( kEvAddress ); ++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ ++ printk( "%sI have been assigned address: %d\n", pszMe, address ); ++ break; ++ ++ ++ case SET_CONFIGURATION: ++ if ( req.wValue == 1 ) { ++ /* configured */ ++ if (usbctl_next_state_on_event( kEvConfig ) != kError){ ++ /* (re)set the out and in max packet sizes */ ++ desc_t * pDesc = sa1100_usb_get_descriptor_ptr(); ++ __u32 out = __le16_to_cpu( pDesc->b.ep1.wMaxPacketSize ); ++ __u32 in = __le16_to_cpu( pDesc->b.ep2.wMaxPacketSize ); ++ Ser0UDCOMP = ( out - 1 ); ++ Ser0UDCIMP = ( in - 1 ); ++ printk( "%sConfigured (OMP=%8.8X IMP=%8.8X)\n", pszMe, out, in ); ++ } ++ } else if ( req.wValue == 0 ) { ++ /* de-configured */ ++ if (usbctl_next_state_on_event( kEvDeConfig ) != kError ) ++ printk( "%sDe-Configured\n", pszMe ); ++ } else { ++ printk( "%ssetup phase: Unknown " ++ "\"set configuration\" data %d\n", ++ pszMe, req.wValue ); ++ } ++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ ++ break; ++ ++ case CLEAR_FEATURE: ++ /* could check data length, direction...26Jan01ww */ ++ if ( req.wValue == 0 ) { /* clearing ENDPOINT_HALT/STALL */ ++ int ep = windex_to_ep_num( req.wIndex ); ++ if ( ep == 1 ) { ++ printk( "%sclear feature \"endpoint halt\" " ++ " on receiver\n", pszMe ); ++ ep1_reset(); ++ } ++ else if ( ep == 2 ) { ++ printk( "%sclear feature \"endpoint halt\" " ++ "on xmitter\n", pszMe ); ++ ep2_reset(); ++ } else { ++ printk( "%sclear feature \"endpoint halt\" " ++ "on unsupported ep # %d\n", ++ pszMe, ep ); ++ } ++ } else { ++ printk( "%sUnsupported feature selector (%d) " ++ "in clear feature. Ignored.\n" , ++ pszMe, req.wValue ); ++ } ++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ ++ break; ++ ++ case SET_FEATURE: ++ if ( req.wValue == 0 ) { /* setting ENDPOINT_HALT/STALL */ ++ int ep = windex_to_ep_num( req.wValue ); ++ if ( ep == 1 ) { ++ printk( "%set feature \"endpoint halt\" " ++ "on receiver\n", pszMe ); ++ ep1_stall(); ++ } ++ else if ( ep == 2 ) { ++ printk( "%sset feature \"endpoint halt\" " ++ " on xmitter\n", pszMe ); ++ ep2_stall(); ++ } else { ++ printk( "%sset feature \"endpoint halt\" " ++ "on unsupported ep # %d\n", ++ pszMe, ep ); ++ } ++ } ++ else { ++ printk( "%sUnsupported feature selector " ++ "(%d) in set feature\n", ++ pszMe, req.wValue ); ++ } ++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */ ++ break; ++ ++ ++ /* The rest have a data phase that writes back to the host */ ++ case GET_STATUS: ++ /* return status bit flags */ ++ status_buf[0] = status_buf[1] = 0; ++ n = request_target(req.bmRequestType); ++ switch( n ) { ++ case kTargetDevice: ++ if ( self_powered_hook() ) ++ status_buf[0] |= 1; ++ break; ++ case kTargetInterface: ++ break; ++ case kTargetEndpoint: ++ /* return stalled bit */ ++ n = windex_to_ep_num( req.wIndex ); ++ if ( n == 1 ) ++ status_buf[0] |= (Ser0UDCCS1 & UDCCS1_FST) >> 4; ++ else if ( n == 2 ) ++ status_buf[0] |= (Ser0UDCCS2 & UDCCS2_FST) >> 5; ++ else { ++ printk( "%sUnknown endpoint (%d) " ++ "in GET_STATUS\n", pszMe, n ); ++ } ++ break; ++ default: ++ printk( "%sUnknown target (%d) in GET_STATUS\n", ++ pszMe, n ); ++ /* fall thru */ ++ break; ++ } ++ cs_bits = queue_and_start_write( status_buf, ++ req.wLength, ++ sizeof( status_buf ) ); ++ set_cs_bits( cs_bits ); ++ break; ++ case GET_DESCRIPTOR: ++ get_descriptor( &req ); ++ break; ++ ++ case GET_CONFIGURATION: ++ status_buf[0] = (usbd_info.state == USB_STATE_CONFIGURED) ++ ? 1 ++ : 0; ++ cs_bits = queue_and_start_write( status_buf, req.wLength, 1 ); ++ set_cs_bits( cs_bits ); ++ break; ++ case GET_INTERFACE: ++ printk( "%sfixme: get interface not supported\n", pszMe ); ++ cs_bits = queue_and_start_write( NULL, req.wLength, 0 ); ++ set_cs_bits( cs_bits ); ++ break; ++ case SET_INTERFACE: ++ printk( "%sfixme: set interface not supported\n", pszMe ); ++ set_cs_bits( UDCCS0_DE | UDCCS0_SO ); ++ break; ++ default : ++ printk("%sunknown request 0x%x\n", pszMe, req.bRequest); ++ break; ++ } /* switch( bRequest ) */ ++ ++sh_sb_end: ++ return; ++ ++} ++/* ++ * common_wrtie_preamble() ++ * Called before execution of sh_write() or sh_write_with_empty_packet() ++ * Handles common abort conditions. ++ * ++ */ ++static void common_write_preamble( void ) ++{ ++ /* If "setup end" has been set, the usb controller has ++ ..terminated a setup transaction before we set DE. This ++ ..happens during enumeration with some hosts. For example, ++ ..the host will ask for our device descriptor and specify ++ ..a return of 64 bytes. When we hand back the first 8, the ++ ..host will know our max packet size and turn around and ++ ..issue a new setup immediately. This causes the UDC to auto-ack ++ ..the new setup and set SE. We must then "unload" (process) ++ ..the new setup, which is what will happen after this preamble ++ ..is finished executing. ++ */ ++ __u32 cs_reg_in = Ser0UDCCS0; ++ ++ if ( cs_reg_in & UDCCS0_SE ) { ++ PRINTKD( "%swrite_preamble(): Early termination of setup\n", pszMe ); ++ Ser0UDCCS0 = UDCCS0_SSE; /* clear setup end */ ++ current_handler = sh_setup_begin; ++ } ++ ++ if ( cs_reg_in & UDCCS0_SST ) { ++ PRINTKD( "%swrite_preamble(): UDC sent stall\n", pszMe ); ++ Ser0UDCCS0 = UDCCS0_SST; /* clear setup end */ ++ current_handler = sh_setup_begin; ++ } ++ ++ if ( cs_reg_in & UDCCS0_OPR ) { ++ PRINTKD( "%swrite_preamble(): see OPR. Stopping write to " ++ "handle new SETUP\n", pszMe ); ++ /* very rarely, you can get OPR and leftover IPR. Try to clear */ ++ UDC_clear( Ser0UDCCS0, UDCCS0_IPR ); ++ current_handler = sh_setup_begin; ++ } ++} ++ ++/* ++ * sh_write() ++ * This is the setup handler when we are in the data return phase of ++ * a setup request and have as much (or more) data than the host ++ * requested. If we enter this routine and bytes left is zero, the ++ * last data packet has gone (int is because IPR was just cleared) ++ * so we just set DE and reset. Otheriwse, we write another packet ++ * and set IPR. ++ */ ++static void sh_write() ++{ ++ PRINTKD( "W\n" ); ++ ++ if ( Ser0UDCCS0 & UDCCS0_IPR ) { ++ PRINTKD( "%ssh_write(): IPR set, exiting\n", pszMe ); ++ return; ++ } ++ ++ /* If bytes left is zero, we are coming in on the ++ ..interrupt after the last packet went out. And ++ ..we know we don't have to empty packet this transfer ++ ..so just set DE and we are done */ ++ ++ if ( 0 == wr.bytes_left ) { ++ /* that's it, so data end */ ++ set_de(); ++ wr.p = NULL; /* be anal */ ++ current_handler = sh_setup_begin; ++ } else { ++ /* Otherwise, more data to go */ ++ write_fifo(); ++ set_ipr(); ++ } ++} ++/* ++ * sh_write_with_empty_packet() ++ * This is the setup handler when we don't have enough data to ++ * satisfy the host's request. After we send everything we've got ++ * we must send an empty packet (by setting IPR and DE) so the ++ * host can perform "short packet retirement" and not stall. ++ * ++ */ ++static void sh_write_with_empty_packet( void ) ++{ ++ __u32 cs_reg_out = 0; ++ PRINTKD( "WE\n" ); ++ ++ if ( Ser0UDCCS0 & UDCCS0_IPR ) { ++ PRINTKD( "%ssh_write(): IPR set, exiting\n", pszMe ); ++ return; ++ } ++ ++ /* If bytes left is zero, we are coming in on the ++ ..interrupt after the last packet went out. ++ ..we must do short packet suff, so set DE and IPR ++ */ ++ ++ if ( 0 == wr.bytes_left ) { ++ set_ipr_and_de(); ++ wr.p = NULL; ++ current_handler = sh_setup_begin; ++ PRINTKD( "%ssh_write empty() Sent empty packet \n", pszMe ); ++ } else { ++ write_fifo(); /* send data */ ++ set_ipr(); /* flag a packet is ready */ ++ } ++ Ser0UDCCS0 = cs_reg_out; ++} ++ ++/*************************************************************************** ++Other Private Subroutines ++***************************************************************************/ ++/* ++ * queue_and_start_write() ++ * p == data to send ++ * req == bytes host requested ++ * act == bytes we actually have ++ * Returns: bits to be flipped in ep0 control/status register ++ * ++ * Called from sh_setup_begin() to begin a data return phase. Sets up the ++ * global "wr"-ite structure and load the outbound FIFO with data. ++ * If can't send all the data, set appropriate handler for next interrupt. ++ * ++ */ ++static __u32 queue_and_start_write( void * in, int req, int act ) ++{ ++ __u32 cs_reg_bits = UDCCS0_IPR; ++ unsigned char * p = (unsigned char*) in; ++ ++ PRINTKD( "Qr=%d a=%d\n",req,act ); ++ ++ /* thou shalt not enter data phase until the serviced OUT is clear */ ++ if ( ! clear_opr() ) { ++ printk( "%sSO did not clear OPR\n", pszMe ); ++ return ( UDCCS0_DE | UDCCS0_SO ) ; ++ } ++ wr.p = p; ++ wr.bytes_left = MIN( act, req ); ++ ++ write_fifo(); ++ ++ if ( 0 == wr.bytes_left ) { ++ cs_reg_bits |= UDCCS0_DE; /* out in 1 so data end */ ++ wr.p = NULL; /* be anal */ ++ } ++ else if ( act < req ) /* we are going to short-change host */ ++ current_handler = sh_write_with_empty_packet; /* so need nul to not stall */ ++ else /* we have as much or more than requested */ ++ current_handler = sh_write; ++ ++ return cs_reg_bits; /* note: IPR was set uncondtionally at start of routine */ ++} ++/* ++ * write_fifo() ++ * Stick bytes in the 8 bytes endpoint zero FIFO. ++ * This version uses a variety of tricks to make sure the bytes ++ * are written correctly. 1. The count register is checked to ++ * see if the byte went in, and the write is attempted again ++ * if not. 2. An overall counter is used to break out so we ++ * don't hang in those (rare) cases where the UDC reverses ++ * direction of the FIFO underneath us without notification ++ * (in response to host aborting a setup transaction early). ++ * ++ */ ++static void write_fifo( void ) ++{ ++ int bytes_this_time = MIN( wr.bytes_left, 8 ); ++ int bytes_written = 0; ++ int i=0; ++ ++ PRINTKD( "WF=%d: ", bytes_this_time ); ++ ++ while( bytes_this_time-- ) { ++ PRINTKD( "%2.2X ", *wr.p ); ++ i = 0; ++ do { ++ Ser0UDCD0 = *wr.p; ++ udelay( 20 ); /* voodo 28Feb01ww */ ++ i++; ++ } while( Ser0UDCWC == bytes_written && i < 10 ); ++ if ( i == 50 ) { ++ printk( "%swrite_fifo: write failure\n", pszMe ); ++ usbd_info.stats.ep0_fifo_write_failures++; ++ } ++ ++ wr.p++; ++ bytes_written++; ++ } ++ wr.bytes_left -= bytes_written; ++ ++ /* following propagation voodo so maybe caller writing IPR in ++ ..a moment might actually get it to stick 28Feb01ww */ ++ udelay( 300 ); ++ ++ usbd_info.stats.ep0_bytes_written += bytes_written; ++ PRINTKD( "L=%d WCR=%8.8X\n", wr.bytes_left, Ser0UDCWC ); ++} ++/* ++ * read_fifo() ++ * Read 1-8 bytes out of FIFO and put in request. ++ * Called to do the initial read of setup requests ++ * from the host. Return number of bytes read. ++ * ++ * Like write fifo above, this driver uses multiple ++ * reads checked agains the count register with an ++ * overall timeout. ++ * ++ */ ++static int ++read_fifo( usb_dev_request_t * request ) ++{ ++ int bytes_read = 0; ++ int fifo_count; ++ int i; ++ ++ unsigned char * pOut = (unsigned char*) request; ++ ++ fifo_count = ( Ser0UDCWC & 0xFF ); ++ ++ ASSERT( fifo_count <= 8 ); ++ PRINTKD( "RF=%d ", fifo_count ); ++ ++ while( fifo_count-- ) { ++ i = 0; ++ do { ++ *pOut = (unsigned char) Ser0UDCD0; ++ udelay( 10 ); ++ } while( ( Ser0UDCWC & 0xFF ) != fifo_count && i < 10 ); ++ if ( i == 10 ) { ++ printk( "%sread_fifo(): read failure\n", pszMe ); ++ usbd_info.stats.ep0_fifo_read_failures++; ++ } ++ pOut++; ++ bytes_read++; ++ } ++ ++ PRINTKD( "fc=%d\n", bytes_read ); ++ usbd_info.stats.ep0_bytes_read++; ++ return bytes_read; ++} ++ ++/* ++ * get_descriptor() ++ * Called from sh_setup_begin to handle data return ++ * for a GET_DESCRIPTOR setup request. ++ */ ++static void get_descriptor( usb_dev_request_t * pReq ) ++{ ++ __u32 cs_bits = 0; ++ string_desc_t * pString; ++ ep_desc_t * pEndpoint; ++ ++ desc_t * pDesc = sa1100_usb_get_descriptor_ptr(); ++ int type = pReq->wValue >> 8; ++ int idx = pReq->wValue & 0xFF; ++ ++ switch( type ) { ++ case USB_DESC_DEVICE: ++ cs_bits = ++ queue_and_start_write( &pDesc->dev, ++ pReq->wLength, ++ pDesc->dev.bLength ); ++ break; ++ ++ // return config descriptor buffer, cfg, intf, 2 ep ++ case USB_DESC_CONFIG: ++ cs_bits = ++ queue_and_start_write( &pDesc->b, ++ pReq->wLength, ++ sizeof( struct cdb ) ); ++ break; ++ ++ // not quite right, since doesn't do language code checking ++ case USB_DESC_STRING: ++ pString = sa1100_usb_get_string_descriptor( idx ); ++ if ( pString ) { ++ if ( idx != 0 ) { // if not language index ++ printk( "%sReturn string %d: ", pszMe, idx ); ++ psdesc( pString ); ++ } ++ cs_bits = ++ queue_and_start_write( pString, ++ pReq->wLength, ++ pString->bLength ); ++ } ++ else { ++ printk("%sunkown string index %d Stall.\n", pszMe, idx ); ++ cs_bits = ( UDCCS0_DE | UDCCS0_SO | UDCCS0_FST ); ++ } ++ break; ++ ++ case USB_DESC_INTERFACE: ++ if ( idx == pDesc->b.intf.bInterfaceNumber ) { ++ cs_bits = ++ queue_and_start_write( &pDesc->b.intf, ++ pReq->wLength, ++ pDesc->b.intf.bLength ); ++ } ++ break; ++ ++ case USB_DESC_ENDPOINT: /* correct? 21Feb01ww */ ++ if ( idx == 1 ) ++ pEndpoint = &pDesc->b.ep1; ++ else if ( idx == 2 ) ++ pEndpoint = &pDesc->b.ep2; ++ else ++ pEndpoint = NULL; ++ if ( pEndpoint ) { ++ cs_bits = ++ queue_and_start_write( pEndpoint, ++ pReq->wLength, ++ pEndpoint->bLength ); ++ } else { ++ printk("%sunkown endpoint index %d Stall.\n", pszMe, idx ); ++ cs_bits = ( UDCCS0_DE | UDCCS0_SO | UDCCS0_FST ); ++ } ++ break; ++ ++ ++ default : ++ printk("%sunknown descriptor type %d. Stall.\n", pszMe, type ); ++ cs_bits = ( UDCCS0_DE | UDCCS0_SO | UDCCS0_FST ); ++ break; ++ ++ } ++ set_cs_bits( cs_bits ); ++} ++ ++ ++/* some voodo I am adding, since the vanilla macros just aren't doing it 1Mar01ww */ ++ ++#define ABORT_BITS ( UDCCS0_SST | UDCCS0_SE ) ++#define OK_TO_WRITE (!( Ser0UDCCS0 & ABORT_BITS )) ++#define BOTH_BITS (UDCCS0_IPR | UDCCS0_DE) ++ ++static void set_cs_bits( __u32 bits ) ++{ ++ if ( bits & ( UDCCS0_SO | UDCCS0_SSE | UDCCS0_FST ) ) ++ Ser0UDCCS0 = bits; ++ else if ( (bits & BOTH_BITS) == BOTH_BITS ) ++ set_ipr_and_de(); ++ else if ( bits & UDCCS0_IPR ) ++ set_ipr(); ++ else if ( bits & UDCCS0_DE ) ++ set_de(); ++} ++ ++static void set_de( void ) ++{ ++ int i = 1; ++ while( 1 ) { ++ if ( OK_TO_WRITE ) { ++ Ser0UDCCS0 |= UDCCS0_DE; ++ } else { ++ PRINTKD( "%sQuitting set DE because SST or SE set\n", pszMe ); ++ break; ++ } ++ if ( Ser0UDCCS0 & UDCCS0_DE ) ++ break; ++ udelay( i ); ++ if ( ++i == 50 ) { ++ printk( "%sDangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8X)\n", ++ pszMe, UDCCS0_DE, Ser0UDCCS0 ); ++ break; ++ } ++ } ++} ++ ++static void set_ipr( void ) ++{ ++ int i = 1; ++ while( 1 ) { ++ if ( OK_TO_WRITE ) { ++ Ser0UDCCS0 |= UDCCS0_IPR; ++ } else { ++ PRINTKD( "%sQuitting set IPR because SST or SE set\n", pszMe ); ++ break; ++ } ++ if ( Ser0UDCCS0 & UDCCS0_IPR ) ++ break; ++ udelay( i ); ++ if ( ++i == 50 ) { ++ printk( "%sDangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8X)\n", ++ pszMe, UDCCS0_IPR, Ser0UDCCS0 ); ++ break; ++ } ++ } ++} ++ ++ ++ ++static void set_ipr_and_de( void ) ++{ ++ int i = 1; ++ while( 1 ) { ++ if ( OK_TO_WRITE ) { ++ Ser0UDCCS0 |= BOTH_BITS; ++ } else { ++ PRINTKD( "%sQuitting set IPR/DE because SST or SE set\n", pszMe ); ++ break; ++ } ++ if ( (Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS) ++ break; ++ udelay( i ); ++ if ( ++i == 50 ) { ++ printk( "%sDangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8X)\n", ++ pszMe, UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0 ); ++ break; ++ } ++ } ++} ++ ++static bool clear_opr( void ) ++{ ++ int i = 10000; ++ bool is_clear; ++ do { ++ Ser0UDCCS0 = UDCCS0_SO; ++ is_clear = ! ( Ser0UDCCS0 & UDCCS0_OPR ); ++ if ( i-- <= 0 ) { ++ printk( "%sclear_opr(): failed\n", pszMe ); ++ break; ++ } ++ } while( ! is_clear ); ++ return is_clear; ++} ++ ++ ++ ++ ++ ++/* end usb_ep0.c */ ++ +diff -urN linux-2.4.26/arch/arm/mach-sa1100/usb_recv.c linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb_recv.c +--- linux-2.4.26/arch/arm/mach-sa1100/usb_recv.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb_recv.c 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,205 @@ ++/* ++ * Generic receive layer for the SA1100 USB client function ++ * Copyright (c) 2001 by Nicolas Pitre ++ * ++ * This code was loosely inspired by the original version which was ++ * Copyright (c) Compaq Computer Corporation, 1998-1999 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This is still work in progress... ++ * ++ * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "sa1100_usb.h" ++#include "usb_ctl.h" ++ ++ ++static char *ep1_buf; ++static int ep1_len; ++static usb_callback_t ep1_callback; ++static char *ep1_curdmabuf; ++static dma_addr_t ep1_curdmapos; ++static int ep1_curdmalen; ++static int ep1_remain; ++static int dmachn_rx; ++static int rx_pktsize; ++ ++static int naking; ++ ++static void ++ep1_start(void) ++{ ++ sa1100_dma_flush_all(dmachn_rx); ++ if (!ep1_curdmalen) { ++ ep1_curdmalen = rx_pktsize; ++ if (ep1_curdmalen > ep1_remain) ++ ep1_curdmalen = ep1_remain; ++ ep1_curdmapos = pci_map_single(NULL, ep1_curdmabuf, ep1_curdmalen, ++ PCI_DMA_FROMDEVICE); ++ } ++ sa1100_dma_queue_buffer(dmachn_rx, NULL, ep1_curdmapos, ep1_curdmalen); ++ if ( naking ) { ++ /* turn off NAK of OUT packets, if set */ ++ UDC_flip( Ser0UDCCS1, UDCCS1_RPC ); ++ naking = 0; ++ } ++} ++ ++static void ++ep1_done(int flag) ++{ ++ int size = ep1_len - ep1_remain; ++ ++ if (!ep1_len) ++ return; ++ if (ep1_curdmalen) ++ pci_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen, ++ PCI_DMA_FROMDEVICE); ++ ep1_len = ep1_curdmalen = 0; ++ if (ep1_callback) { ++ ep1_callback(flag, size); ++ } ++} ++ ++void ++ep1_state_change_notify( int new_state ) ++{ ++ ++} ++ ++void ++ep1_stall( void ) ++{ ++ /* SET_FEATURE force stall at UDC */ ++ UDC_set( Ser0UDCCS1, UDCCS1_FST ); ++} ++ ++int ++ep1_init(int chn) ++{ ++ desc_t * pd = sa1100_usb_get_descriptor_ptr(); ++ rx_pktsize = __le16_to_cpu( pd->b.ep1.wMaxPacketSize ); ++ dmachn_rx = chn; ++ sa1100_dma_flush_all(dmachn_rx); ++ ep1_done(-EAGAIN); ++ return 0; ++} ++ ++void ++ep1_reset(void) ++{ ++ desc_t * pd = sa1100_usb_get_descriptor_ptr(); ++ rx_pktsize = __le16_to_cpu( pd->b.ep1.wMaxPacketSize ); ++ sa1100_dma_flush_all(dmachn_rx); ++ UDC_clear(Ser0UDCCS1, UDCCS1_FST); ++ ep1_done(-EINTR); ++} ++ ++void ++ep1_int_hndlr(int udcsr) ++{ ++ dma_addr_t dma_addr; ++ unsigned int len; ++ int status = Ser0UDCCS1; ++ ++ if ( naking ) printk( "%sEh? in ISR but naking = %d\n", "usbrx: ", naking ); ++ ++ if (status & UDCCS1_RPC) { ++ ++ if (!ep1_curdmalen) { ++ printk("usb_recv: RPC for non-existent buffer\n"); ++ naking=1; ++ return; ++ } ++ ++ sa1100_dma_stop(dmachn_rx); ++ ++ if (status & UDCCS1_SST) { ++ printk("usb_recv: stall sent OMP=%d\n",Ser0UDCOMP); ++ UDC_flip(Ser0UDCCS1, UDCCS1_SST); ++ ep1_done(-EIO); // UDC aborted current transfer, so we do ++ return; ++ } ++ ++ if (status & UDCCS1_RPE) { ++ printk("usb_recv: RPError %x\n", status); ++ UDC_flip(Ser0UDCCS1, UDCCS1_RPC); ++ ep1_done(-EIO); ++ return; ++ } ++ ++ sa1100_dma_get_current(dmachn_rx, NULL, &dma_addr); ++ pci_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen, ++ PCI_DMA_FROMDEVICE); ++ len = dma_addr - ep1_curdmapos; ++ if (len < ep1_curdmalen) { ++ char *buf = ep1_curdmabuf + len; ++ while (Ser0UDCCS1 & UDCCS1_RNE) { ++ if (len >= ep1_curdmalen) { ++ printk("usb_recv: too much data in fifo\n"); ++ break; ++ } ++ *buf++ = Ser0UDCDR; ++ len++; ++ } ++ } else if (Ser0UDCCS1 & UDCCS1_RNE) { ++ printk("usb_recv: fifo screwed, shouldn't contain data\n"); ++ len = 0; ++ } ++ ep1_curdmalen = 0; /* dma unmap already done */ ++ ep1_remain -= len; ++ naking = 1; ++ ep1_done((len) ? 0 : -EPIPE); ++ } ++ /* else, you can get here if we are holding NAK */ ++} ++ ++int ++sa1100_usb_recv(char *buf, int len, usb_callback_t callback) ++{ ++ int flags; ++ ++ if (ep1_len) ++ return -EBUSY; ++ ++ local_irq_save(flags); ++ ep1_buf = buf; ++ ep1_len = len; ++ ep1_callback = callback; ++ ep1_remain = len; ++ ep1_curdmabuf = buf; ++ ep1_curdmalen = 0; ++ ep1_start(); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL(sa1100_usb_recv); ++ ++void ++sa1100_usb_recv_reset(void) ++{ ++ ep1_reset(); ++} ++ ++EXPORT_SYMBOL(sa1100_usb_recv_reset); ++ ++void ++sa1100_usb_recv_stall(void) ++{ ++ ep1_stall(); ++} ++ ++EXPORT_SYMBOL(sa1100_usb_recv_stall); ++ +diff -urN linux-2.4.26/arch/arm/mach-sa1100/usb_send.c linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb_send.c +--- linux-2.4.26/arch/arm/mach-sa1100/usb_send.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mach-sa1100/usb_send.c 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,205 @@ ++/* ++ * Generic xmit layer for the SA1100 USB client function ++ * Copyright (c) 2001 by Nicolas Pitre ++ * ++ * This code was loosely inspired by the original version which was ++ * Copyright (c) Compaq Computer Corporation, 1998-1999 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This is still work in progress... ++ * ++ * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. ++ * 15/03/2001 - ep2_start now sets UDCAR to overcome something that is hardware ++ * bug, I think. green@iXcelerator.com ++ */ ++ ++#include ++#include ++#include ++#include // for the massive_attack hack 28Feb01ww ++#include ++#include ++#include ++#include ++ ++#include "sa1100_usb.h" ++#include "usb_ctl.h" ++ ++ ++static char *ep2_buf; ++static int ep2_len; ++static usb_callback_t ep2_callback; ++static dma_addr_t ep2_dma; ++static dma_addr_t ep2_curdmapos; ++static int ep2_curdmalen; ++static int ep2_remain; ++static int dmachn_tx; ++static int tx_pktsize; ++ ++/* device state is changing, async */ ++void ++ep2_state_change_notify( int new_state ) ++{ ++} ++ ++/* set feature stall executing, async */ ++void ++ep2_stall( void ) ++{ ++ UDC_set( Ser0UDCCS2, UDCCS2_FST ); /* force stall at UDC */ ++} ++ ++static void ++ep2_start(void) ++{ ++ if (!ep2_len) ++ return; ++ ++ ep2_curdmalen = tx_pktsize; ++ if (ep2_curdmalen > ep2_remain) ++ ep2_curdmalen = ep2_remain; ++ ++ /* must do this _before_ queue buffer.. */ ++ UDC_flip( Ser0UDCCS2,UDCCS2_TPC ); /* stop NAKing IN tokens */ ++ UDC_write( Ser0UDCIMP, ep2_curdmalen-1 ); ++ ++ /* Remove if never seen...8Mar01ww */ ++ { ++ int massive_attack = 20; ++ while ( Ser0UDCIMP != ep2_curdmalen-1 && massive_attack-- ) { ++ printk( "usbsnd: Oh no you don't! Let me spin..." ); ++ udelay( 500 ); ++ printk( "and try again...\n" ); ++ UDC_write( Ser0UDCIMP, ep2_curdmalen-1 ); ++ } ++ if ( massive_attack != 20 ) { ++ if ( Ser0UDCIMP != ep2_curdmalen-1 ) ++ printk( "usbsnd: Massive attack FAILED :-( %d\n", ++ 20 - massive_attack ); ++ else ++ printk( "usbsnd: Massive attack WORKED :-) %d\n", ++ 20 - massive_attack ); ++ } ++ } ++ /* End remove if never seen... 8Mar01ww */ ++ ++ Ser0UDCAR = usbd_info.address; // fighting stupid silicon bug ++ sa1100_dma_queue_buffer(dmachn_tx, NULL, ep2_curdmapos, ep2_curdmalen); ++} ++ ++static void ++ep2_done(int flag) ++{ ++ int size = ep2_len - ep2_remain; ++ if (ep2_len) { ++ pci_unmap_single(NULL, ep2_dma, ep2_len, PCI_DMA_TODEVICE); ++ ep2_len = 0; ++ if (ep2_callback) ++ ep2_callback(flag, size); ++ } ++} ++ ++int ++ep2_init(int chn) ++{ ++ desc_t * pd = sa1100_usb_get_descriptor_ptr(); ++ tx_pktsize = __le16_to_cpu( pd->b.ep2.wMaxPacketSize ); ++ dmachn_tx = chn; ++ sa1100_dma_flush_all(dmachn_tx); ++ ep2_done(-EAGAIN); ++ return 0; ++} ++ ++void ++ep2_reset(void) ++{ ++ desc_t * pd = sa1100_usb_get_descriptor_ptr(); ++ tx_pktsize = __le16_to_cpu( pd->b.ep2.wMaxPacketSize ); ++ UDC_clear(Ser0UDCCS2, UDCCS2_FST); ++ sa1100_dma_flush_all(dmachn_tx); ++ ep2_done(-EINTR); ++} ++ ++void ++ep2_int_hndlr(int udcsr) ++{ ++ int status = Ser0UDCCS2; ++ ++ if (Ser0UDCAR != usbd_info.address) // check for stupid silicon bug. ++ Ser0UDCAR = usbd_info.address; ++ ++ UDC_flip(Ser0UDCCS2, UDCCS2_SST); ++ ++ if (status & UDCCS2_TPC) { ++ sa1100_dma_flush_all(dmachn_tx); ++ ++ if (status & (UDCCS2_TPE | UDCCS2_TUR)) { ++ printk("usb_send: transmit error %x\n", status); ++ ep2_done(-EIO); ++ } else { ++#if 1 // 22Feb01ww/Oleg ++ ep2_curdmapos += ep2_curdmalen; ++ ep2_remain -= ep2_curdmalen; ++#else ++ ep2_curdmapos += Ser0UDCIMP + 1; // this is workaround ++ ep2_remain -= Ser0UDCIMP + 1; // for case when setting of Ser0UDCIMP was failed ++#endif ++ ++ if (ep2_remain != 0) { ++ ep2_start(); ++ } else { ++ ep2_done(0); ++ } ++ } ++ } else { ++ printk("usb_send: Not TPC: UDCCS2 = %x\n", status); ++ } ++} ++ ++int ++sa1100_usb_send(char *buf, int len, usb_callback_t callback) ++{ ++ int flags; ++ ++ if (usbd_info.state != USB_STATE_CONFIGURED) ++ return -ENODEV; ++ ++ if (ep2_len) ++ return -EBUSY; ++ ++ local_irq_save(flags); ++ ep2_buf = buf; ++ ep2_len = len; ++ ep2_dma = pci_map_single(NULL, buf, len, PCI_DMA_TODEVICE); ++ ep2_callback = callback; ++ ep2_remain = len; ++ ep2_curdmapos = ep2_dma; ++ ep2_start(); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++ ++void ++sa1100_usb_send_reset(void) ++{ ++ ep2_reset(); ++} ++ ++int sa1100_usb_xmitter_avail( void ) ++{ ++ if (usbd_info.state != USB_STATE_CONFIGURED) ++ return -ENODEV; ++ if (ep2_len) ++ return -EBUSY; ++ return 0; ++} ++ ++ ++EXPORT_SYMBOL(sa1100_usb_xmitter_avail); ++EXPORT_SYMBOL(sa1100_usb_send); ++EXPORT_SYMBOL(sa1100_usb_send_reset); +diff -urN linux-2.4.26/arch/arm/mm/Makefile linux-2.4.26-vrs1/arch/arm/mm/Makefile +--- linux-2.4.26/arch/arm/mm/Makefile 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/Makefile 2004-01-14 21:38:43.000000000 +0000 +@@ -39,6 +39,8 @@ + p-$(CONFIG_CPU_ARM925T) += proc-arm925.o + p-$(CONFIG_CPU_ARM926T) += proc-arm926.o + p-$(CONFIG_CPU_ARM1020) += proc-arm1020.o ++p-$(CONFIG_CPU_ARM1020E) += proc-arm1020E.o ++p-$(CONFIG_CPU_ARM1022) += proc-arm1022.o + p-$(CONFIG_CPU_ARM1026) += proc-arm1026.o + p-$(CONFIG_CPU_SA110) += proc-sa110.o + p-$(CONFIG_CPU_SA1100) += proc-sa110.o +diff -urN linux-2.4.26/arch/arm/mm/alignment.c linux-2.4.26-vrs1/arch/arm/mm/alignment.c +--- linux-2.4.26/arch/arm/mm/alignment.c 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/alignment.c 2004-04-09 15:09:44.000000000 +0100 +@@ -11,7 +11,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -19,7 +18,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -30,13 +28,11 @@ + #include + #include + +-extern void +-do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr, +- int error_code, struct pt_regs *regs); ++#include "fault.h" + + /* + * 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998 +- * /proc/sys/debug/alignment, modified and integrated into ++ * /proc/cpu/alignment, modified and integrated into + * Linux 2.1 by Russell King + * + * Speed optimisations and better fault handling by Russell King. +@@ -130,31 +126,6 @@ + return count; + } + +-/* +- * This needs to be done after sysctl_init, otherwise sys/ will be +- * overwritten. Actually, this shouldn't be in sys/ at all since +- * it isn't a sysctl, and it doesn't contain sysctl information. +- * We now locate it in /proc/cpu/alignment instead. +- */ +-static int __init alignment_init(void) +-{ +- struct proc_dir_entry *res; +- +- res = proc_mkdir("cpu", NULL); +- if (!res) +- return -ENOMEM; +- +- res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res); +- if (!res) +- return -ENOMEM; +- +- res->read_proc = proc_alignment_read; +- res->write_proc = proc_alignment_write; +- +- return 0; +-} +- +-__initcall(alignment_init); + #endif /* CONFIG_PROC_FS */ + + union offset_union { +@@ -429,7 +400,7 @@ + * For alignment faults on the ARM922T/ARM920T the MMU makes + * the FSR (and hence addr) equal to the updated base address + * of the multiple access rather than the restored value. +- * Switch this messsage off if we've got a ARM92[02], otherwise ++ * Switch this message off if we've got a ARM92[02], otherwise + * [ls]dm alignment faults are noisy! + */ + #if !(defined CONFIG_CPU_ARM922T) && !(defined CONFIG_CPU_ARM920T) +@@ -486,7 +457,8 @@ + return TYPE_ERROR; + } + +-int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs) ++static int ++do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) + { + union offset_union offset; + unsigned long instr, instrptr; +@@ -541,7 +513,7 @@ + case SHIFT_RORRRX: + if (shiftval == 0) { + offset.un >>= 1; +- if (regs->ARM_cpsr & CC_C_BIT) ++ if (regs->ARM_cpsr & PSR_C_BIT) + offset.un |= 1 << 31; + } else + offset.un = offset.un >> shiftval | +@@ -577,7 +549,7 @@ + /* + * We got a fault - fix it up, or die. + */ +- do_bad_area(current, current->mm, addr, error_code, regs); ++ do_bad_area(current, current->mm, addr, fsr, regs); + return 0; + + bad: +@@ -594,8 +566,8 @@ + + if (ai_usermode & 1) + printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%08lx " +- "Address=0x%08lx Code 0x%02x\n", current->comm, +- current->pid, instrptr, instr, addr, error_code); ++ "Address=0x%08lx FSR 0x%03x\n", current->comm, ++ current->pid, instrptr, instr, addr, fsr); + + if (ai_usermode & 2) + goto fixup; +@@ -607,3 +579,34 @@ + + return 0; + } ++ ++/* ++ * This needs to be done after sysctl_init, otherwise sys/ will be ++ * overwritten. Actually, this shouldn't be in sys/ at all since ++ * it isn't a sysctl, and it doesn't contain sysctl information. ++ * We now locate it in /proc/cpu/alignment instead. ++ */ ++static int __init alignment_init(void) ++{ ++#ifdef CONFIG_PROC_FS ++ struct proc_dir_entry *res; ++ ++ res = proc_mkdir("cpu", NULL); ++ if (!res) ++ return -ENOMEM; ++ ++ res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res); ++ if (!res) ++ return -ENOMEM; ++ ++ res->read_proc = proc_alignment_read; ++ res->write_proc = proc_alignment_write; ++#endif ++ ++ hook_fault_code(1, do_alignment, SIGILL, "alignment exception"); ++ hook_fault_code(3, do_alignment, SIGILL, "alignment exception"); ++ ++ return 0; ++} ++ ++__initcall(alignment_init); +diff -urN linux-2.4.26/arch/arm/mm/fault-armv.c linux-2.4.26-vrs1/arch/arm/mm/fault-armv.c +--- linux-2.4.26/arch/arm/mm/fault-armv.c 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/fault-armv.c 2004-04-09 21:22:20.000000000 +0100 +@@ -2,116 +2,90 @@ + * linux/arch/arm/mm/fault-armv.c + * + * Copyright (C) 1995 Linus Torvalds +- * Modifications for ARM processor (c) 1995-2001 Russell King ++ * Modifications for ARM processor (c) 1995-2003 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +-#include +-#include + #include + #include +-#include +-#include + #include + #include +-#include + #include +-#include +-#include + #include + #include + +-#include +-#include + #include + #include ++#include + +-extern void show_pte(struct mm_struct *mm, unsigned long addr); +-extern int do_page_fault(unsigned long addr, int error_code, +- struct pt_regs *regs); +-extern int do_translation_fault(unsigned long addr, int error_code, +- struct pt_regs *regs); +-extern void do_bad_area(struct task_struct *tsk, struct mm_struct *mm, +- unsigned long addr, int error_code, +- struct pt_regs *regs); +- +-#ifdef CONFIG_ALIGNMENT_TRAP +-extern int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs); +-#else +-#define do_alignment do_bad +-#endif +- ++#include "fault.h" + + /* + * Some section permission faults need to be handled gracefully. + * They can happen due to a __{get,put}_user during an oops. + */ + static int +-do_sect_fault(unsigned long addr, int error_code, struct pt_regs *regs) ++do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) + { + struct task_struct *tsk = current; +- do_bad_area(tsk, tsk->active_mm, addr, error_code, regs); ++ do_bad_area(tsk, tsk->active_mm, addr, fsr, regs); + return 0; + } + + /* +- * Hook for things that need to trap external faults. Note that +- * we don't guarantee that this will be the final version of the +- * interface. +- */ +-int (*external_fault)(unsigned long addr, struct pt_regs *regs); +- +-static int +-do_external_fault(unsigned long addr, int error_code, struct pt_regs *regs) +-{ +- if (external_fault) +- return external_fault(addr, regs); +- return 1; +-} +- +-/* + * This abort handler always returns "fault". + */ + static int +-do_bad(unsigned long addr, int error_code, struct pt_regs *regs) ++do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) + { + return 1; + } + +-static const struct fsr_info { +- int (*fn)(unsigned long addr, int error_code, struct pt_regs *regs); ++static struct fsr_info { ++ int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs); + int sig; + const char *name; + } fsr_info[] = { + { do_bad, SIGSEGV, "vector exception" }, +- { do_alignment, SIGILL, "alignment exception" }, ++ { do_bad, SIGILL, "alignment exception" }, + { do_bad, SIGKILL, "terminal exception" }, +- { do_alignment, SIGILL, "alignment exception" }, +- { do_external_fault, SIGBUS, "external abort on linefetch" }, ++ { do_bad, SIGILL, "alignment exception" }, ++ { do_bad, SIGBUS, "external abort on linefetch" }, + { do_translation_fault, SIGSEGV, "section translation fault" }, +- { do_external_fault, SIGBUS, "external abort on linefetch" }, ++ { do_bad, SIGBUS, "external abort on linefetch" }, + { do_page_fault, SIGSEGV, "page translation fault" }, +- { do_external_fault, SIGBUS, "external abort on non-linefetch" }, ++ { do_bad, SIGBUS, "external abort on non-linefetch" }, + { do_bad, SIGSEGV, "section domain fault" }, +- { do_external_fault, SIGBUS, "external abort on non-linefetch" }, ++ { do_bad, SIGBUS, "external abort on non-linefetch" }, + { do_bad, SIGSEGV, "page domain fault" }, + { do_bad, SIGBUS, "external abort on translation" }, + { do_sect_fault, SIGSEGV, "section permission fault" }, + { do_bad, SIGBUS, "external abort on translation" }, +- { do_page_fault, SIGSEGV, "page permission fault" } ++ { do_page_fault, SIGSEGV, "page permission fault" }, + }; + ++void __init ++hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), ++ int sig, const char *name) ++{ ++ if (nr >= 0 && nr < ARRAY_SIZE(fsr_info)) { ++ fsr_info[nr].fn = fn; ++ fsr_info[nr].sig = sig; ++ fsr_info[nr].name = name; ++ } ++} ++ + /* + * Dispatch a data abort to the relevant handler. + */ + asmlinkage void +-do_DataAbort(unsigned long addr, int error_code, struct pt_regs *regs, int fsr) ++do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) + { + const struct fsr_info *inf = fsr_info + (fsr & 15); + +- if (!inf->fn(addr, error_code, regs)) ++ if (!inf->fn(addr, fsr, regs)) + return; + + printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n", +@@ -127,25 +101,28 @@ + do_translation_fault(addr, 0, regs); + } + ++static unsigned long shared_pte_mask = L_PTE_CACHEABLE; ++ + /* + * We take the easy way out of this problem - we make the + * PTE uncacheable. However, we leave the write buffer on. + */ +-static void adjust_pte(struct vm_area_struct *vma, unsigned long address) ++static int adjust_pte(struct vm_area_struct *vma, unsigned long address) + { + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte, entry; ++ int ret = 0; + + pgd = pgd_offset(vma->vm_mm, address); + if (pgd_none(*pgd)) +- return; ++ goto no_pgd; + if (pgd_bad(*pgd)) + goto bad_pgd; + + pmd = pmd_offset(pgd, address); + if (pmd_none(*pmd)) +- return; ++ goto no_pmd; + if (pmd_bad(*pmd)) + goto bad_pmd; + +@@ -156,33 +133,38 @@ + * If this page isn't present, or is already setup to + * fault (ie, is old), we can safely ignore any issues. + */ +- if (pte_present(entry) && pte_val(entry) & L_PTE_CACHEABLE) { ++ if (pte_present(entry) && pte_val(entry) & shared_pte_mask) { + flush_cache_page(vma, address); +- pte_val(entry) &= ~L_PTE_CACHEABLE; ++ pte_val(entry) &= ~shared_pte_mask; + set_pte(pte, entry); + flush_tlb_page(vma, address); ++ ret = 1; + } +- return; ++ return ret; + + bad_pgd: + pgd_ERROR(*pgd); + pgd_clear(pgd); +- return; ++no_pgd: ++ return 0; + + bad_pmd: + pmd_ERROR(*pmd); + pmd_clear(pmd); +- return; ++no_pmd: ++ return 0; + } + + static void +-make_coherent(struct vm_area_struct *vma, unsigned long addr, struct page *page) ++make_coherent(struct vm_area_struct *vma, unsigned long addr, struct page *page, int dirty) + { + struct vm_area_struct *mpnt; + struct mm_struct *mm = vma->vm_mm; +- unsigned long pgoff = (addr - vma->vm_start) >> PAGE_SHIFT; ++ unsigned long pgoff; + int aliases = 0; + ++ pgoff = vma->vm_pgoff + ((addr - vma->vm_start) >> PAGE_SHIFT); ++ + /* + * If we have any shared mappings that are in the same mm + * space, then we need to handle them specially to maintain +@@ -194,7 +176,7 @@ + + /* + * If this VMA is not in our MM, we can ignore it. +- * Note that we intentionally don't mask out the VMA ++ * Note that we intentionally mask out the VMA + * that we are fixing up. + */ + if (mpnt->vm_mm != mm || mpnt == vma) +@@ -210,14 +192,17 @@ + if (off >= (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT) + continue; + ++ off = mpnt->vm_start + (off << PAGE_SHIFT); ++ + /* + * Ok, it is within mpnt. Fix it up. + */ +- adjust_pte(mpnt, mpnt->vm_start + (off << PAGE_SHIFT)); +- aliases ++; ++ aliases += adjust_pte(mpnt, off); + } + if (aliases) + adjust_pte(vma, addr); ++ else if (dirty) ++ flush_cache_page(vma, addr); + } + + /* +@@ -242,11 +227,85 @@ + return; + page = pfn_to_page(pfn); + if (page->mapping) { +- if (test_and_clear_bit(PG_dcache_dirty, &page->flags)) { ++ int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags); ++ ++ if (dirty) { + unsigned long kvirt = (unsigned long)page_address(page); + cpu_cache_clean_invalidate_range(kvirt, kvirt + PAGE_SIZE, 0); + } + +- make_coherent(vma, addr, page); ++ make_coherent(vma, addr, page, dirty); ++ } ++} ++ ++/* ++ * Check whether the write buffer has physical address aliasing ++ * issues. If it has, we need to avoid them for the case where ++ * we have several shared mappings of the same object in user ++ * space. ++ */ ++static int __init check_writebuffer(unsigned long *p1, unsigned long *p2) ++{ ++ register unsigned long zero = 0, one = 1, val; ++ ++ mb(); ++ *p1 = one; ++ mb(); ++ *p2 = zero; ++ mb(); ++ val = *p1; ++ mb(); ++ return val != zero; ++} ++ ++static inline void *map_page(struct page *page) ++{ ++ void *map; ++ ++ map = __ioremap(page_to_phys(page), PAGE_SIZE, L_PTE_BUFFERABLE); ++ if (map) ++ get_page(page); ++ return map; ++} ++ ++static inline void unmap_page(void *map) ++{ ++ iounmap(map); ++} ++ ++void __init check_writebuffer_bugs(void) ++{ ++ struct page *page; ++ const char *reason; ++ unsigned long v = 1; ++ ++ printk(KERN_INFO "CPU: Testing write buffer: "); ++ ++ page = alloc_page(GFP_KERNEL); ++ if (page) { ++ unsigned long *p1, *p2; ++ ++ p1 = map_page(page); ++ p2 = map_page(page); ++ ++ if (p1 && p2) { ++ v = check_writebuffer(p1, p2); ++ reason = "enabling work-around"; ++ } else { ++ reason = "unable to map memory\n"; ++ } ++ ++ unmap_page(p1); ++ unmap_page(p2); ++ put_page(page); ++ } else { ++ reason = "unable to grab page\n"; ++ } ++ ++ if (v) { ++ printk("FAIL - %s\n", reason); ++ shared_pte_mask |= L_PTE_BUFFERABLE; ++ } else { ++ printk("pass\n"); + } + } +diff -urN linux-2.4.26/arch/arm/mm/fault-common.c linux-2.4.26-vrs1/arch/arm/mm/fault-common.c +--- linux-2.4.26/arch/arm/mm/fault-common.c 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/fault-common.c 2004-01-14 21:38:43.000000000 +0000 +@@ -11,21 +11,17 @@ + #include + #include + #include +-#include +-#include + #include +-#include + #include +-#include + #include + #include +-#include + #include + + #include +-#include + #include +-#include ++#include ++ ++#include "fault.h" + + #ifdef CONFIG_CPU_26 + #define FAULT_CODE_WRITE 0x02 +@@ -34,13 +30,11 @@ + #define READ_FAULT(m) (!((m) & FAULT_CODE_WRITE)) + #else + /* +- * On 32-bit processors, we define "mode" to be zero when reading, +- * non-zero when writing. This now ties up nicely with the polarity +- * of the 26-bit machines, and also means that we avoid the horrible +- * gcc code for "int val = !other_val;". ++ * "code" is actually the FSR register. Bit 11 set means the ++ * instruction was performing a write. + */ +-#define DO_COW(m) (m) +-#define READ_FAULT(m) (!(m)) ++#define DO_COW(code) ((code) & (1 << 11)) ++#define READ_FAULT(code) (!DO_COW(code)) + #endif + + /* +@@ -54,16 +48,17 @@ + if (!mm) + mm = &init_mm; + +- printk(KERN_ALERT "mm = %p pgd = %p\n", mm, mm->pgd); +- + fs = get_fs(); + set_fs(get_ds()); ++ + do { +- pgd_t pg, *pgd = pgd_offset(mm, addr); ++ pgd_t pg, *pgd; + pmd_t pm, *pmd; + pte_t pt, *pte; + +- printk(KERN_ALERT "*pgd = "); ++ printk(KERN_ALERT "pgd = %p\n", mm->pgd); ++ pgd = pgd_offset(mm, addr); ++ printk(KERN_ALERT "[%08lx] *pgd=", addr); + + if (__get_user(pgd_val(pg), (unsigned long *)pgd)) { + printk("(faulted)"); +@@ -122,7 +117,7 @@ + * Oops. The kernel tried to access some page that wasn't present. + */ + static void +-__do_kernel_fault(struct mm_struct *mm, unsigned long addr, int error_code, ++__do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, + struct pt_regs *regs) + { + unsigned long fixup; +@@ -148,7 +143,7 @@ + "paging request", addr); + + show_pte(mm, addr); +- die("Oops", regs, error_code); ++ die("Oops", regs, fsr); + do_exit(SIGKILL); + } + +@@ -157,20 +152,20 @@ + * User mode accesses just cause a SIGSEGV + */ + static void +-__do_user_fault(struct task_struct *tsk, unsigned long addr, int error_code, +- int code, struct pt_regs *regs) ++__do_user_fault(struct task_struct *tsk, unsigned long addr, ++ unsigned int fsr, int code, struct pt_regs *regs) + { + struct siginfo si; + + #ifdef CONFIG_DEBUG_USER + printk(KERN_DEBUG "%s: unhandled page fault at pc=0x%08lx, " + "lr=0x%08lx (bad address=0x%08lx, code %d)\n", +- tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, error_code); ++ tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, fsr); + show_regs(regs); + #endif + + tsk->thread.address = addr; +- tsk->thread.error_code = error_code; ++ tsk->thread.error_code = fsr; + tsk->thread.trap_no = 14; + si.si_signo = SIGSEGV; + si.si_errno = 0; +@@ -181,20 +176,20 @@ + + void + do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr, +- int error_code, struct pt_regs *regs) ++ unsigned int fsr, struct pt_regs *regs) + { + /* + * If we are in kernel mode at this point, we + * have no context to handle this fault with. + */ + if (user_mode(regs)) +- __do_user_fault(tsk, addr, error_code, SEGV_MAPERR, regs); ++ __do_user_fault(tsk, addr, fsr, SEGV_MAPERR, regs); + else +- __do_kernel_fault(mm, addr, error_code, regs); ++ __do_kernel_fault(mm, addr, fsr, regs); + } + + static int +-__do_page_fault(struct mm_struct *mm, unsigned long addr, int error_code, ++__do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, + struct task_struct *tsk) + { + struct vm_area_struct *vma; +@@ -212,7 +207,7 @@ + * memory access, so we can handle it. + */ + good_area: +- if (READ_FAULT(error_code)) /* read? */ ++ if (READ_FAULT(fsr)) /* read? */ + mask = VM_READ|VM_EXEC; + else + mask = VM_WRITE; +@@ -227,7 +222,7 @@ + * than endlessly redo the fault. + */ + survive: +- fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(error_code)); ++ fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(fsr)); + + /* + * Handle the "normal" cases first - successful and sigbus +@@ -260,7 +255,7 @@ + return fault; + } + +-int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs) ++int do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) + { + struct task_struct *tsk; + struct mm_struct *mm; +@@ -277,7 +272,7 @@ + goto no_context; + + down_read(&mm->mmap_sem); +- fault = __do_page_fault(mm, addr, error_code, tsk); ++ fault = __do_page_fault(mm, addr, fsr, tsk); + up_read(&mm->mmap_sem); + + /* +@@ -308,7 +303,7 @@ + printk("VM: killing process %s\n", tsk->comm); + do_exit(SIGKILL); + } else +- __do_user_fault(tsk, addr, error_code, fault == -1 ? ++ __do_user_fault(tsk, addr, fsr, fault == -1 ? + SEGV_ACCERR : SEGV_MAPERR, regs); + return 0; + +@@ -323,7 +318,7 @@ + * or user mode. + */ + tsk->thread.address = addr; +- tsk->thread.error_code = error_code; ++ tsk->thread.error_code = fsr; + tsk->thread.trap_no = 14; + force_sig(SIGBUS, tsk); + #ifdef CONFIG_DEBUG_USER +@@ -336,7 +331,7 @@ + return 0; + + no_context: +- __do_kernel_fault(mm, addr, error_code, regs); ++ __do_kernel_fault(mm, addr, fsr, regs); + return 0; + } + +@@ -357,21 +352,23 @@ + * interrupt or a critical region, and should only copy the information + * from the master page table, nothing more. + */ +-int do_translation_fault(unsigned long addr, int error_code, struct pt_regs *regs) ++int do_translation_fault(unsigned long addr, unsigned int fsr, ++ struct pt_regs *regs) + { + struct task_struct *tsk; +- struct mm_struct *mm; + int offset; + pgd_t *pgd, *pgd_k; + pmd_t *pmd, *pmd_k; + + if (addr < TASK_SIZE) +- return do_page_fault(addr, error_code, regs); ++ return do_page_fault(addr, fsr, regs); + + offset = __pgd_offset(addr); + + /* + * FIXME: CP15 C1 is write only on ARMv3 architectures. ++ * You really need to read the value in the page table ++ * register, not a copy. + */ + pgd = cpu_get_pgd() + offset; + pgd_k = init_mm.pgd + offset; +@@ -395,8 +392,7 @@ + + bad_area: + tsk = current; +- mm = tsk->active_mm; + +- do_bad_area(tsk, mm, addr, error_code, regs); ++ do_bad_area(tsk, tsk->active_mm, addr, fsr, regs); + return 0; + } +diff -urN linux-2.4.26/arch/arm/mm/fault.h linux-2.4.26-vrs1/arch/arm/mm/fault.h +--- linux-2.4.26/arch/arm/mm/fault.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/fault.h 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,9 @@ ++void do_bad_area(struct task_struct *tsk, struct mm_struct *mm, ++ unsigned long addr, unsigned int fsr, struct pt_regs *regs); ++ ++void show_pte(struct mm_struct *mm, unsigned long addr); ++ ++int do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs); ++ ++int do_translation_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs); ++ +diff -urN linux-2.4.26/arch/arm/mm/init.c linux-2.4.26-vrs1/arch/arm/mm/init.c +--- linux-2.4.26/arch/arm/mm/init.c 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/init.c 2004-01-14 21:32:24.000000000 +0000 +@@ -9,7 +9,6 @@ + */ + #include + #include +-#include + #include + #include + #include +@@ -18,7 +17,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff -urN linux-2.4.26/arch/arm/mm/ioremap.c linux-2.4.26-vrs1/arch/arm/mm/ioremap.c +--- linux-2.4.26/arch/arm/mm/ioremap.c 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/ioremap.c 2004-02-25 22:03:38.000000000 +0000 +@@ -144,7 +144,7 @@ + */ + offset = phys_addr & ~PAGE_MASK; + phys_addr &= PAGE_MASK; +- size = PAGE_ALIGN(last_addr) - phys_addr; ++ size = PAGE_ALIGN(last_addr + 1) - phys_addr; + + /* + * Ok, go for it.. +diff -urN linux-2.4.26/arch/arm/mm/mm-armv.c linux-2.4.26-vrs1/arch/arm/mm/mm-armv.c +--- linux-2.4.26/arch/arm/mm/mm-armv.c 2003-11-28 18:26:19.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/arm/mm/mm-armv.c 2004-01-14 21:38:43.000000000 +0000 +@@ -9,7 +9,6 @@ + * + * Page table sludge for ARM v3 and v4 processor architectures. + */ +-#include + #include + #include + #include +@@ -390,6 +389,9 @@ + init_maps->bufferable = 0; + + create_mapping(init_maps); ++ ++ flush_cache_all(); ++ flush_tlb_all(); + } + + /* +diff -urN linux-2.4.26/arch/arm/mm/proc-arm1020.S linux-2.4.26-vrs1/arch/arm/mm/proc-arm1020.S +--- linux-2.4.26/arch/arm/mm/proc-arm1020.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/proc-arm1020.S 2004-01-14 21:32:24.000000000 +0000 +@@ -65,18 +65,21 @@ + * + * Returns: + * r0 = address of abort +- * r1 != 0 if writing +- * r3 = FSR ++ * r1 = FSR ++ * r3 = corrupted + * r4 = corrupted + */ + .align 5 + ENTRY(cpu_arm1020_data_abort) +- mrc p15, 0, r3, c5, c0, 0 @ get FSR ++ mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR +- ldr r1, [r2] @ read aborted instruction +- and r3, r3, #255 +- tst r1, r1, lsr #21 @ C = bit 20 +- sbc r1, r1, r1 @ r1 = C - 1 ++ tst r3, #PSR_T_BIT ++ ldrneh r3, [r2] @ read aborted thumb instruction ++ ldreq r3, [r2] @ read aborted ARM instruction ++ bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR ++ movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 ++ tst r3, #1 << 20 @ check write ++ orreq r1, r1, #1 << 11 + mov pc, lr + + /* +@@ -170,10 +173,10 @@ + #endif + subs r3, r3, #1 + cmp r3, #0 +- bge 2b @ entries 3F to 0 ++ bhs 2b @ entries 3F to 0 + subs r1, r1, #1 + cmp r1, #0 +- bge 1b @ segments 7 to 0 ++ bhs 1b @ segments 7 to 0 + #endif + + #ifndef CONFIG_CPU_ICACHE_DISABLE +@@ -201,7 +204,7 @@ + bic r0, r0, #DCACHELINESIZE - 1 + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE +- bgt cpu_arm1020_cache_clean_invalidate_all_r2 ++ bhi cpu_arm1020_cache_clean_invalidate_all_r2 + mcr p15, 0, r3, c7, c10, 4 + #ifndef CONFIG_CPU_DCACHE_DISABLE + 1: mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry +@@ -214,7 +217,7 @@ + #endif + add r0, r0, #DCACHELINESIZE + cmp r0, r1 +- blt 1b ++ blo 1b + #endif + + #ifndef CONFIG_CPU_ICACHE_DISABLE +@@ -302,7 +305,7 @@ + #endif + add r0, r0, #DCACHELINESIZE + cmp r0, r1 +- blt 1b ++ blo 1b + #else + /* D cache off, but still drain the write buffer */ + mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer +@@ -328,7 +331,7 @@ + bic r0, r0, #DCACHELINESIZE - 1 + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE +- bgt cpu_arm1020_cache_clean_invalidate_all_r2 ++ bhi cpu_arm1020_cache_clean_invalidate_all_r2 + mcr p15, 0, r3, c7, c10, 4 + #ifndef CONFIG_CPU_DCACHE_DISABLE + 1: mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry +@@ -341,7 +344,7 @@ + #endif + add r0, r0, #DCACHELINESIZE + cmp r0, r1 +- blt 1b ++ blo 1b + #endif + + #ifndef CONFIG_CPU_BPREDICT_DISABLE +@@ -488,7 +491,7 @@ + mov r0, r0 + #endif + cmp r0, r1 +- blt 1b ++ blo 1b + mov pc, lr + + /* +@@ -541,10 +544,10 @@ + #endif + subs r3, r3, #1 + cmp r3, #0 +- bge 2b @ entries 3F to 0 ++ bhs 2b @ entries 3F to 0 + subs r1, r1, #1 + cmp r1, #0 +- bge 1b @ segments 15 to 0 ++ bhs 1b @ segments 15 to 0 + + #endif + mov r1, #0 +@@ -603,7 +606,7 @@ + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + +- tst r1, #LPTE_USER | LPTE_EXEC @ User or Exec? ++ tst r1, #LPTE_USER @ User? + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? +@@ -740,12 +743,12 @@ + + .type cpu_arch_name, #object + cpu_arch_name: +- .asciz "armv4" ++ .asciz "armv5t" + .size cpu_arch_name, . - cpu_arch_name + + .type cpu_elf_name, #object + cpu_elf_name: +- .asciz "v4" ++ .asciz "v5" + .size cpu_elf_name, . - cpu_elf_name + .align + +@@ -753,9 +756,9 @@ + + .type __arm1020_proc_info,#object + __arm1020_proc_info: +- .long 0x4100a200 +- .long 0xff00fff0 +- .long 0x00000c02 @ mmuflags ++ .long 0x4104a200 @ ARM 1020T (Architecture v5T) ++ .long 0xff0ffff0 ++ .long 0x00000c0e @ mmuflags + b __arm1020_setup + .long cpu_arch_name + .long cpu_elf_name +diff -urN linux-2.4.26/arch/arm/mm/proc-arm1020E.S linux-2.4.26-vrs1/arch/arm/mm/proc-arm1020E.S +--- linux-2.4.26/arch/arm/mm/proc-arm1020E.S 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/proc-arm1020E.S 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,718 @@ ++/* ++ * linux/arch/arm/mm/proc-arm1020E.S: MMU functions for ARM1020E ++ * ++ * Copyright (C) 2000 ARM Limited ++ * Copyright (C) 2000 Deep Blue Solutions Ltd. ++ * ++ * 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 ++ * ++ * ++ * These are the low level assembler for performing cache and TLB ++ * functions on the arm1020E. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * This is the maximum size of an area which will be invalidated ++ * using the single invalidate entry instructions. Anything larger ++ * than this, and we go for the whole cache. ++ * ++ * This value should be chosen such that we choose the cheapest ++ * alternative. ++ */ ++#define MAX_AREA_SIZE 32768 ++ ++/* ++ * the cache line size of the I and D cache ++ */ ++#define DCACHELINESIZE 32 ++#define ICACHELINESIZE 32 ++ ++/* ++ * and the page size ++ */ ++#define LOG2PAGESIZE 12 /* == 4096 Bytes */ ++#define PAGESIZE (1 << LOG2PAGESIZE) ++ ++/* ++ * create some useful conditional macro definitions ++ * we often need to know if we are ((not dcache disable) and writethrough) or ((not dcache disable) and writeback) ++ */ ++#ifdef CONFIG_CPU_DCACHE_DISABLE ++ #undef CONFIG_CPU_DCACHE_WRITETHROUGH ++ #undef CONFIG_CPU_DCACHE_WRITEBACK ++ #undef CONFIG_CPU_DCACHE_ENABLE ++#else ++ #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ #undef CONFIG_CPU_DCACHE_WRITEBACK ++ #else ++ #define CONFIG_CPU_DCACHE_WRITEBACK ++ #endif ++ #define CONFIG_CPU_DCACHE_ENABLE ++#endif ++ ++#ifdef CONFIG_CPU_ICACHE_DISABLE ++ #undef CONFIG_CPU_ICACHE_ENABLE ++#else ++ #define CONFIG_CPU_ICACHE_ENABLE ++#endif ++ ++ .text ++ ++/* ++ * cpu_arm1020E_data_abort() ++ * ++ * obtain information about current aborted instruction ++ * Note: we read user space. This means we might cause a data ++ * abort here if the I-TLB and D-TLB aren't seeing the same ++ * picture. Unfortunately, this does happen. We live with it. ++ * ++ * r2 = address of aborted instruction ++ * r3 = cpsr ++ * ++ * Returns: ++ * r0 = address of abort ++ * r1 = FSR ++ * r3 = corrupted ++ * r4 = corrupted ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_data_abort) ++ mrc p15, 0, r1, c5, c0, 0 @ get FSR ++ mrc p15, 0, r0, c6, c0, 0 @ get FAR ++ tst r3, #PSR_T_BIT ++ ldrneh r3, [r2] @ read aborted thumb instruction ++ ldreq r3, [r2] @ read aborted ARM instruction ++ bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR ++ movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 ++ tst r3, #1 << 20 @ check write ++ orreq r1, r1, #1 << 11 ++ mov pc, lr ++ ++/* ++ * cpu_arm1020E_check_bugs() ++ */ ++ENTRY(cpu_arm1020E_check_bugs) ++ mrs ip, cpsr ++ bic ip, ip, #F_BIT ++ msr cpsr, ip ++ mov pc, lr ++ ++/* ++ * cpu_arm1020E_proc_init() ++ */ ++ENTRY(cpu_arm1020E_proc_init) ++ mov pc, lr ++ ++/* ++ * cpu_arm1020E_proc_fin() ++ */ ++ENTRY(cpu_arm1020E_proc_fin) ++ stmfd sp!, {lr} ++ mov ip, #F_BIT | I_BIT | SVC_MODE ++ msr cpsr_c, ip ++ bl cpu_arm1020E_cache_clean_invalidate_all ++ mrc p15, 0, r0, c1, c0, 0 @ ctrl register ++ bic r0, r0, #0x1000 @ ...i............ ++ bic r0, r0, #0x000e @ ............wca. ++ mcr p15, 0, r0, c1, c0, 0 @ disable caches ++ ldmfd sp!, {pc} ++ ++/* ++ * cpu_arm1020E_reset(loc) ++ * ++ * Perform a soft reset of the system. Put the CPU into the ++ * same state as it would be if it had been reset, and branch ++ * to what would be the reset vector. ++ * ++ * loc: location to jump to for soft reset ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_reset) ++ mov ip, #0 ++ mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches ++ mcr p15, 0, ip, c7, c10, 4 @ drain WB ++ mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs ++ mrc p15, 0, ip, c1, c0, 0 @ ctrl register ++ bic ip, ip, #0x000f @ ............wcam ++ bic ip, ip, #0x1100 @ ...i...s........ ++ mcr p15, 0, ip, c1, c0, 0 @ ctrl register ++ mov pc, r0 ++ ++/* ++ * cpu_arm1020E_do_idle() ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_do_idle) ++ mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt ++ mov pc, lr ++ ++/* ================================= CACHE ================================ */ ++ ++ ++/* ++ * cpu_arm1020E_cache_clean_invalidate_all() ++ * ++ * clean and invalidate all cache lines ++ * ++ * Note: ++ * 1. we should preserve r0 and ip at all times ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_cache_clean_invalidate_all) ++ mov r2, #1 ++cpu_arm1020E_cache_clean_invalidate_all_r2: ++ ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mov r1, #0x0F << 5 @ 16 segments ++1: orr r3, r1, #63 << 26 @ 64 entries ++2: mcr p15, 0, r3, c7, c14, 2 @ clean and invalidate D index ++ subs r3, r3, #1 << 26 ++ bcs 2b ++ subs r1, r1, #1 << 5 ++ bcs 1b @ segments 15 to 0 ++#endif ++ ++ mov r1, #0 ++ ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ mcr p15, 0, r1, c7, c6, 0 @ invalidate D cache ++#endif ++ ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ teq r2, #0 ++ mcrne p15, 0, r1, c7, c5, 0 @ invalidate I cache ++#endif ++ mcr p15, 0, r1, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ++ * cpu_arm1020E_cache_clean_invalidate_range(start, end, flags) ++ * ++ * clean and invalidate all cache lines associated with this area of memory ++ * ++ * start: Area start address ++ * end: Area end address ++ * flags: nonzero for I cache as well ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_cache_clean_invalidate_range) ++ bic r0, r0, #DCACHELINESIZE - 1 ++ sub r3, r1, r0 ++ cmp r3, #MAX_AREA_SIZE ++ bhs cpu_arm1020E_cache_clean_invalidate_all_r2 ++1: ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry ++#endif ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry ++#endif ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ teq r2, #0 ++ mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry ++#endif ++ add r0, r0, #DCACHELINESIZE ++ cmp r0, r1 ++ bls 1b @ unsigned lower or same - must include end point (r1)! ++ ++ mov r1, #0 ++ mcr p15, 0, r1, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ++ * cpu_arm1020E_flush_ram_page(page) ++ * ++ * clean and invalidate all cache lines associated with this area of memory ++ * ++ * page: page to clean and invalidate ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_flush_ram_page) ++ mov r1, #PAGESIZE ++ mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page ++ mov r0, r0, LSL #LOG2PAGESIZE ++1: ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry ++#endif ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry ++#endif ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry ++#endif ++ add r0, r0, #DCACHELINESIZE ++ subs r1, r1, #DCACHELINESIZE ++ bne 1b ++ ++ mcr p15, 0, r1, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ================================ D-CACHE =============================== */ ++ ++/* ++ * cpu_arm1020E_dcache_invalidate_range(start, end) ++ * ++ * throw away all D-cached data in specified region without an obligation ++ * to write them back. Note however that we must clean the D-cached entries ++ * around the boundaries if the start and/or end address are not cache ++ * aligned. ++ * ++ * start: virtual start address ++ * end: virtual end address ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_dcache_invalidate_range) ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ bic r0, r0, #DCACHELINESIZE - 1 ++#endif ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ tst r0, #DCACHELINESIZE - 1 ++ bic r0, r0, #DCACHELINESIZE - 1 ++ mcrne p15, 0, r0, c7, c10, 1 @ clean D entry at start ++ tst r1, #DCACHELINESIZE - 1 ++ mcrne p15, 0, r1, c7, c10, 1 @ clean D entry at end ++#endif ++ ++1: ++#ifdef CONFIG_CPU_DCACHE_ENABLE ++ mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry ++#endif ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry ++#endif ++ add r0, r0, #DCACHELINESIZE ++ cmp r0, r1 ++ bls 1b ++ ++ /* Even if the D cache is off still drain the write buffer */ ++ mov r0, #0 ++ mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer ++ mov pc, lr ++ ++/* ++ * cpu_arm1020E_dcache_clean_range(start, end) ++ * ++ * For the specified virtual address range, ensure that all caches contain ++ * clean data, such that peripheral accesses to the physical RAM fetch ++ * correct data. ++ * ++ * start: virtual start address ++ * end: virtual end address ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_dcache_clean_range) ++ ++ mov r2, #0 ++ ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ bic r0, r0, #DCACHELINESIZE - 1 ++ sub r3, r1, r0 ++ cmp r3, #MAX_AREA_SIZE ++ bhs cpu_arm1020E_cache_clean_invalidate_all_r2 ++ ++1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry ++ add r0, r0, #DCACHELINESIZE ++ cmp r0, r1 ++ bls 1b ++#endif ++ ++ mcr p15, 0, r2, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ++ * cpu_arm1020E_dcache_clean_page(page) ++ * ++ * Cleans a single page of dcache so that if we have any future aliased ++ * mappings, they will be consistent at the time that they are created. ++ * ++ * page: virtual address of page to clean from dcache ++ * ++ * Note: ++ * we don't invalidate the entries since when we write the page ++ * out to disk, the entries may get reloaded into the cache. ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_dcache_clean_page) ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page ++ mov r0, r0, LSL #LOG2PAGESIZE ++ mov r1, #PAGESIZE ++1: ++ mcr p15, 0, r0, c7, c10, 1 @ clean D entry ++ add r0, r0, #DCACHELINESIZE ++ subs r1, r1, #DCACHELINESIZE ++ bne 1b ++#endif ++ mov r1, #0 ++ mcr p15, 0, r1, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ++ * cpu_arm1020E_dcache_clean_entry(addr) ++ * ++ * Clean the specified entry of any caches such that the MMU ++ * translation fetches will obtain correct data. ++ * ++ * addr: cache-unaligned virtual address ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_dcache_clean_entry) ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ bic r0, r0, #DCACHELINESIZE - 1 ++ mcr p15, 0, r0, c7, c10, 1 @ clean single D entry ++#endif ++ mov r1, #0 ++ mcr p15, 0, r1, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ================================ I-CACHE =============================== */ ++ ++/* ++ * cpu_arm1020E_icache_invalidate_range(start, end) ++ * ++ * invalidate a range of virtual addresses from the Icache ++ * ++ * This is a little misleading, it is not intended to clean out ++ * the i-cache but to make sure that any data written to the ++ * range is made consistent. This means that when we execute code ++ * in that region, everything works as we expect. ++ * ++ * This generally means writing back data in the Dcache and ++ * write buffer and invalidating the Icache over that region ++ * ++ * start: virtual start address ++ * end: virtual end address ++ * ++ * NOTE: ICACHELINESIZE == DCACHELINESIZE (so we don't need to ++ * loop twice, once for i-cache, once for d-cache) ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_icache_invalidate_range) ++ bic r0, r0, #ICACHELINESIZE - 1 ++ sub r3, r1, r0 ++ cmp r3, #MAX_AREA_SIZE ++ movhs r2, #1 ++ bhs cpu_arm1020E_cache_clean_invalidate_all_r2 ++ ++1: ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mcr p15, 0, r0, c7, c10, 1 @ Clean D entry ++#endif ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ mcr p15, 0, r0, c7, c5, 1 @ Invalidate I entry ++#endif ++ add r0, r0, #DCACHELINESIZE ++ cmp r0, r1 ++ bls 1b @ unsigned lower or same - includes r1 entry ++ ++ mov r0, #0 ++ mcr p15, 0, r0, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++ENTRY(cpu_arm1020E_icache_invalidate_page) ++ mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page ++ mov r0, r0, LSL #LOG2PAGESIZE ++ add r1, r0, #PAGESIZE ++ b cpu_arm1020E_icache_invalidate_range ++ ++/* ================================== TLB ================================= */ ++ ++/* ++ * cpu_arm1020E_tlb_invalidate_all() ++ * ++ * Invalidate all TLB entries ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_tlb_invalidate_all) ++ mov r0, #0 ++ mcr p15, 0, r0, c7, c10, 4 @ drain WB ++ mcr p15, 0, r0, c8, c7, 0 @ invalidate I & D tlbs ++ mov pc, lr ++ ++/* ++ * cpu_arm1020E_tlb_invalidate_range(start, end) ++ * ++ * invalidate TLB entries covering the specified range ++ * ++ * start: range start address ++ * end: range end address ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_tlb_invalidate_range) ++ sub r3, r1, r0 ++ cmp r3, #256 * PAGESIZE ++ bhs cpu_arm1020E_tlb_invalidate_all ++ mov r3, #0 ++ mcr p15, 0, r3, c7, c10, 4 @ drain WB ++ mov r3, #PAGESIZE ++ sub r3, r3, #1 ++ bic r0, r0, r3 ++1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry ++ mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry ++ add r0, r0, #PAGESIZE ++ cmp r0, r1 ++ bls 1b ++ mov pc, lr ++ ++/* ++ * cpu_arm1020E_tlb_invalidate_page(page, flags) ++ * ++ * invalidate the TLB entries for the specified page. ++ * ++ * page: page to invalidate ++ * flags: non-zero if we include the I TLB ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_tlb_invalidate_page) ++ mov r3, #0 ++ mcr p15, 0, r3, c7, c10, 4 @ drain WB ++ mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page ++ mov r0, r0, LSL #LOG2PAGESIZE ++ teq r1, #0 ++ mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry ++ mcrne p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry ++ mov pc, lr ++ ++/* =============================== PageTable ============================== */ ++ ++/* ++ * cpu_arm1020E_set_pgd(pgd) ++ * ++ * Set the translation base pointer to be as described by pgd. ++ * ++ * pgd: new page tables ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_set_pgd) ++ stmfd sp!, {lr} ++ bl cpu_arm1020E_cache_clean_invalidate_all @ preserves r0 ++ mov r1, #0 ++ mcr p15, 0, r0, c2, c0, 0 @ load page table pointer ++ mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs ++ ldmfd sp!, {pc} ++ ++/* ++ * cpu_arm1020E_set_pmd(pmdp, pmd) ++ * ++ * Set a level 1 translation table entry, and clean it out of ++ * any caches such that the MMUs can load it correctly. ++ * ++ * pmdp: pointer to PMD entry ++ * pmd: PMD value to store ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_set_pmd) ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ eor r2, r1, #0x0a @ C & Section ++ tst r2, #0x0b ++ biceq r1, r1, #4 @ clear bufferable bit ++#endif ++ str r1, [r0] ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mcr p15, 0, r0, c7, c10, 1 @ clean D entry ++#endif ++ mov r0, #0 ++ mcr p15, 0, r0, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ++ * cpu_arm1020E_set_pte(ptep, pte) ++ * ++ * Set a PTE and flush it out ++ */ ++ .align 5 ++ENTRY(cpu_arm1020E_set_pte) ++ str r1, [r0], #-1024 @ linux version ++ ++ eor r1, r1, #LPTE_PRESENT | LPTE_YOUNG | LPTE_WRITE | LPTE_DIRTY ++ ++ bic r2, r1, #0xff0 ++ bic r2, r2, #3 ++ orr r2, r2, #HPTE_TYPE_SMALL ++ ++ tst r1, #LPTE_USER @ User? ++ orrne r2, r2, #HPTE_AP_READ ++ ++ tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? ++ orreq r2, r2, #HPTE_AP_WRITE ++ ++ tst r1, #LPTE_PRESENT | LPTE_YOUNG @ Present and Young? ++ movne r2, #0 ++ ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ eor r3, r2, #0x0a @ C and Small Page? ++ tst r3, #0x0b @ if so.. ++ biceq r2, r2, #0x04 @ clear the bufferable bit ++#endif ++ str r2, [r0] @ hardware version ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mcr p15, 0, r0, c7, c10, 1 @ clean D entry ++#endif ++ mov r1, #0 ++ mcr p15, 0, r1, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++ ++cpu_manu_name: ++ .asciz "ARM" ++ENTRY(cpu_arm1020E_name) ++ .ascii "Arm1020E" ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ .ascii "i" ++#endif ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ .ascii "d" ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ .ascii "(wt)" ++#endif ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ .ascii "(wb)" ++#endif ++#endif ++#ifndef CONFIG_CPU_BPREDICT_DISABLE ++ .ascii "B" ++#endif ++#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN ++ .ascii "RR" ++#endif ++ .ascii "\0" ++ .align ++ ++ .section ".text.init", #alloc, #execinstr ++ ++__arm1020E_setup: ++ mov r0, #0 ++ mcr p15, 0, r0, c7, c7, 0 @ invalidate I,D caches on v4 ++ mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 ++ mcr p15, 0, r0, c8, c7, 0 @ invalidate I,D TLBs on v4 ++ mcr p15, 0, r4, c2, c0, 0 @ load page table pointer ++ mov r0, #0x1f @ Domains 0, 1 = client ++ mcr p15, 0, r0, c3, c0, 0 @ load domain access register ++ ++ mrc p15, 0, r0, c1, c0, 0 @ Read current control register ++/* ++ * The only thing worth keeping from the initial control register is the endian bit ++ */ ++ ++ and r0, r0, #0x0080 @ ........B....... Preserve endian bit, zero all others. ++ orr r0, r0, #0x0070 @ .........111.... Set the SBO (Should Be One) bits ++/* ++ * Turn on what we want. ++ */ ++ orr r0, r0, #0x0001 @ ...............M Enable MMU (Alignment is special cased elsewhere) ++ orr r0, r0, #0x0100 @ .......S........ Enable system MMU protection ++ orr r0, r0, #0x2000 @ ..V............. Enable high vectors ++ ++#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN ++ orr r0, r0, #0x4000 @ .R.............. Force round-robin replacement ++#endif ++ ++#ifndef CONFIG_CPU_BPREDICT_DISABLE ++ orr r0, r0, #0x0800 @ ....Z........... Enable branch prediction ++#endif ++ ++#ifdef CONFIG_CPU_DCACHE_ENABLE ++ orr r0, r0, #0x0004 @ .............C.. Enable D cache ++#endif ++#ifndef CONFIG_CPU_WB_DISABLE ++ orr r0, r0, #0x0008 @ ............W... Write Buffer enabled ++#endif ++ ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ orr r0, r0, #0x1000 @ ...I............ Enable I Cache ++#endif ++ ++ mov pc, lr ++ ++ .text ++ ++/* ++ * Purpose : Function pointers used to access above functions - all calls ++ * come through these ++ */ ++ .type arm1020E_processor_functions, #object ++arm1020E_processor_functions: ++ .word cpu_arm1020E_data_abort ++ .word cpu_arm1020E_check_bugs ++ .word cpu_arm1020E_proc_init ++ .word cpu_arm1020E_proc_fin ++ .word cpu_arm1020E_reset ++ .word cpu_arm1020E_do_idle ++ ++ /* cache */ ++ .word cpu_arm1020E_cache_clean_invalidate_all ++ .word cpu_arm1020E_cache_clean_invalidate_range ++ .word cpu_arm1020E_flush_ram_page ++ ++ /* dcache */ ++ .word cpu_arm1020E_dcache_invalidate_range ++ .word cpu_arm1020E_dcache_clean_range ++ .word cpu_arm1020E_dcache_clean_page ++ .word cpu_arm1020E_dcache_clean_entry ++ ++ /* icache */ ++ .word cpu_arm1020E_icache_invalidate_range ++ .word cpu_arm1020E_icache_invalidate_page ++ ++ /* tlb */ ++ .word cpu_arm1020E_tlb_invalidate_all ++ .word cpu_arm1020E_tlb_invalidate_range ++ .word cpu_arm1020E_tlb_invalidate_page ++ ++ /* pgtable */ ++ .word cpu_arm1020E_set_pgd ++ .word cpu_arm1020E_set_pmd ++ .word cpu_arm1020E_set_pte ++ .size arm1020E_processor_functions, . - arm1020E_processor_functions ++ ++ .type cpu_arm1020E_info, #object ++cpu_arm1020E_info: ++ .long cpu_manu_name ++ .long cpu_arm1020E_name ++ .size cpu_arm1020E_info, . - cpu_arm1020E_info ++ ++ .type cpu_arch_name, #object ++cpu_arch_name: ++ .asciz "armv5te" ++ .size cpu_arch_name, . - cpu_arch_name ++ ++ .type cpu_elf_name, #object ++cpu_elf_name: ++ .asciz "v5" ++ .size cpu_elf_name, . - cpu_elf_name ++ .align ++ ++ .section ".proc.info", #alloc, #execinstr ++ ++ .type __arm1020E_proc_info,#object ++__arm1020E_proc_info: ++ .long 0x4105a200 @ ARM 1020E (Architecture v5TE) ++ .long 0xff0ffff0 ++ .long 0x00000c1e @ mmuflags ++ b __arm1020E_setup ++ .long cpu_arch_name ++ .long cpu_elf_name ++ .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB ++ .long cpu_arm1020E_info ++ .long arm1020E_processor_functions ++ .size __arm1020E_proc_info, . - __arm1020E_proc_info ++ +diff -urN linux-2.4.26/arch/arm/mm/proc-arm1022.S linux-2.4.26-vrs1/arch/arm/mm/proc-arm1022.S +--- linux-2.4.26/arch/arm/mm/proc-arm1022.S 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/proc-arm1022.S 2004-01-14 21:32:24.000000000 +0000 +@@ -0,0 +1,716 @@ ++/* ++ * linux/arch/arm/mm/proc-arm1022.S: MMU functions for ARM1022E ++ * ++ * Copyright (C) 2000 ARM Limited ++ * Copyright (C) 2000 Deep Blue Solutions Ltd. ++ * ++ * 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 ++ * ++ * ++ * These are the low level assembler for performing cache and TLB ++ * functions on the arm1022E. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * This is the maximum size of an area which will be invalidated ++ * using the single invalidate entry instructions. Anything larger ++ * than this, and we go for the whole cache. ++ * ++ * This value should be chosen such that we choose the cheapest ++ * alternative. ++ */ ++#define MAX_AREA_SIZE 16384 ++ ++/* ++ * the cache line size of the I and D cache ++ */ ++#define DCACHELINESIZE 32 ++#define ICACHELINESIZE 32 ++ ++/* ++ * and the page size ++ */ ++#define LOG2PAGESIZE 12 /* == 4096 Bytes */ ++#define PAGESIZE (1 << LOG2PAGESIZE) ++ ++/* ++ * create some useful conditional macro definitions ++ * we often need to know if we are ((not dcache disable) and writethrough) or ((not dcache disable) and writeback) ++ */ ++#ifdef CONFIG_CPU_DCACHE_DISABLE ++ #undef CONFIG_CPU_DCACHE_WRITETHROUGH ++ #undef CONFIG_CPU_DCACHE_WRITEBACK ++ #undef CONFIG_CPU_DCACHE_ENABLE ++#else ++ #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ #undef CONFIG_CPU_DCACHE_WRITEBACK ++ #else ++ #define CONFIG_CPU_DCACHE_WRITEBACK ++ #endif ++ #define CONFIG_CPU_DCACHE_ENABLE ++#endif ++ ++#ifdef CONFIG_CPU_ICACHE_DISABLE ++ #undef CONFIG_CPU_ICACHE_ENABLE ++#else ++ #define CONFIG_CPU_ICACHE_ENABLE ++#endif ++ ++ .text ++ ++/* ++ * cpu_arm1022_data_abort() ++ * ++ * obtain information about current aborted instruction ++ * Note: we read user space. This means we might cause a data ++ * abort here if the I-TLB and D-TLB aren't seeing the same ++ * picture. Unfortunately, this does happen. We live with it. ++ * ++ * r2 = address of aborted instruction ++ * r3 = cpsr ++ * ++ * Returns: ++ * r0 = address of abort ++ * r1 = FSR ++ * r3 = corrupted ++ * r4 = corrupted ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_data_abort) ++ mrc p15, 0, r1, c5, c0, 0 @ get FSR ++ mrc p15, 0, r0, c6, c0, 0 @ get FAR ++ tst r3, #PSR_T_BIT ++ ldrneh r3, [r2] @ read aborted thumb instruction ++ ldreq r3, [r2] @ read aborted ARM instruction ++ bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR ++ movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 ++ tst r3, #1 << 20 @ check write ++ orreq r1, r1, #1 << 11 ++ mov pc, lr ++ ++/* ++ * cpu_arm1022_check_bugs() ++ */ ++ENTRY(cpu_arm1022_check_bugs) ++ mrs ip, cpsr ++ bic ip, ip, #F_BIT ++ msr cpsr, ip ++ mov pc, lr ++ ++/* ++ * cpu_arm1022_proc_init() ++ */ ++ENTRY(cpu_arm1022_proc_init) ++ mov pc, lr ++ ++/* ++ * cpu_arm1022_proc_fin() ++ */ ++ENTRY(cpu_arm1022_proc_fin) ++ stmfd sp!, {lr} ++ mov ip, #F_BIT | I_BIT | SVC_MODE ++ msr cpsr_c, ip ++ bl cpu_arm1022_cache_clean_invalidate_all ++ mrc p15, 0, r0, c1, c0, 0 @ ctrl register ++ bic r0, r0, #0x1000 @ ...i............ ++ bic r0, r0, #0x000e @ ............wca. ++ mcr p15, 0, r0, c1, c0, 0 @ disable caches ++ ldmfd sp!, {pc} ++ ++/* ++ * cpu_arm1022_reset(loc) ++ * ++ * Perform a soft reset of the system. Put the CPU into the ++ * same state as it would be if it had been reset, and branch ++ * to what would be the reset vector. ++ * ++ * loc: location to jump to for soft reset ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_reset) ++ mov ip, #0 ++ mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches ++ mcr p15, 0, ip, c7, c10, 4 @ drain WB ++ mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs ++ mrc p15, 0, ip, c1, c0, 0 @ ctrl register ++ bic ip, ip, #0x000f @ ............wcam ++ bic ip, ip, #0x1100 @ ...i...s........ ++ mcr p15, 0, ip, c1, c0, 0 @ ctrl register ++ mov pc, r0 ++ ++/* ++ * cpu_arm1022_do_idle() ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_do_idle) ++ mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt ++ mov pc, lr ++ ++/* ================================= CACHE ================================ */ ++ ++ ++/* ++ * cpu_arm1022_cache_clean_invalidate_all() ++ * ++ * clean and invalidate all cache lines ++ * ++ * Note: ++ * 1. we should preserve r0 and ip at all times ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_cache_clean_invalidate_all) ++ mov r2, #1 ++cpu_arm1022_cache_clean_invalidate_all_r2: ++ ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mov r1, #7 << 5 @ 8 segments ++1: orr r3, r1, #63 << 26 @ 64 entries ++2: mcr p15, 0, r3, c7, c14, 2 @ clean and invalidate D index ++ subs r3, r3, #1 << 26 ++ bcs 2b ++ subs r1, r1, #1 << 5 ++ bcs 1b @ segments 7 to 0 ++#endif ++ ++ mov r1, #0 ++ ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ mcr p15, 0, r1, c7, c6, 0 @ invalidate D cache ++#endif ++ ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ teq r2, #0 ++ mcrne p15, 0, r1, c7, c5, 0 @ invalidate I cache ++#endif ++ mcr p15, 0, r1, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ++ * cpu_arm1022_cache_clean_invalidate_range(start, end, flags) ++ * ++ * clean and invalidate all cache lines associated with this area of memory ++ * ++ * start: Area start address ++ * end: Area end address ++ * flags: nonzero for I cache as well ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_cache_clean_invalidate_range) ++ bic r0, r0, #DCACHELINESIZE - 1 ++ sub r3, r1, r0 ++ cmp r3, #MAX_AREA_SIZE ++ bhs cpu_arm1022_cache_clean_invalidate_all_r2 ++1: ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry ++#endif ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry ++#endif ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ teq r2, #0 ++ mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry ++#endif ++ add r0, r0, #DCACHELINESIZE ++ cmp r0, r1 ++ bls 1b @ unsigned lower or same - must include end point (r1)! ++ ++ mov r1, #0 ++ mcr p15, 0, r1, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ++ * cpu_arm1022_flush_ram_page(page) ++ * ++ * clean and invalidate all cache lines associated with this area of memory ++ * ++ * page: page to clean and invalidate ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_flush_ram_page) ++ mov r1, #PAGESIZE ++ mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page ++ mov r0, r0, LSL #LOG2PAGESIZE ++1: ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry ++#endif ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry ++#endif ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry ++#endif ++ add r0, r0, #DCACHELINESIZE ++ subs r1, r1, #DCACHELINESIZE ++ bne 1b ++ ++ mcr p15, 0, r1, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ================================ D-CACHE =============================== */ ++ ++/* ++ * cpu_arm1022_dcache_invalidate_range(start, end) ++ * ++ * throw away all D-cached data in specified region without an obligation ++ * to write them back. Note however that we must clean the D-cached entries ++ * around the boundaries if the start and/or end address are not cache ++ * aligned. ++ * ++ * start: virtual start address ++ * end: virtual end address ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_dcache_invalidate_range) ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ bic r0, r0, #DCACHELINESIZE - 1 ++#endif ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ tst r0, #DCACHELINESIZE - 1 ++ bic r0, r0, #DCACHELINESIZE - 1 ++ mcrne p15, 0, r0, c7, c10, 1 @ clean D entry at start ++ tst r1, #DCACHELINESIZE - 1 ++ mcrne p15, 0, r1, c7, c10, 1 @ clean D entry at end ++#endif ++ ++1: ++#ifdef CONFIG_CPU_DCACHE_ENABLE ++ mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry ++#endif ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry ++#endif ++ add r0, r0, #DCACHELINESIZE ++ cmp r0, r1 ++ bls 1b ++ ++ /* Even if the D cache is off still drain the write buffer */ ++ mov r0, #0 ++ mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer ++ mov pc, lr ++ ++/* ++ * cpu_arm1022_dcache_clean_range(start, end) ++ * ++ * For the specified virtual address range, ensure that all caches contain ++ * clean data, such that peripheral accesses to the physical RAM fetch ++ * correct data. ++ * ++ * start: virtual start address ++ * end: virtual end address ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_dcache_clean_range) ++ ++ mov r2, #0 ++ ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ bic r0, r0, #DCACHELINESIZE - 1 ++ sub r3, r1, r0 ++ cmp r3, #MAX_AREA_SIZE ++ bhs cpu_arm1022_cache_clean_invalidate_all_r2 ++ ++1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry ++ add r0, r0, #DCACHELINESIZE ++ cmp r0, r1 ++ bls 1b ++#endif ++ ++ mcr p15, 0, r2, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ++ * cpu_arm1022_dcache_clean_page(page) ++ * ++ * Cleans a single page of dcache so that if we have any future aliased ++ * mappings, they will be consistent at the time that they are created. ++ * ++ * page: virtual address of page to clean from dcache ++ * ++ * Note: ++ * we don't invalidate the entries since when we write the page ++ * out to disk, the entries may get reloaded into the cache. ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_dcache_clean_page) ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page ++ mov r0, r0, LSL #LOG2PAGESIZE ++ mov r1, #PAGESIZE ++1: ++ mcr p15, 0, r0, c7, c10, 1 @ clean D entry ++ add r0, r0, #DCACHELINESIZE ++ subs r1, r1, #DCACHELINESIZE ++ bne 1b ++#endif ++ mov r1, #0 ++ mcr p15, 0, r1, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ++ * cpu_arm1022_dcache_clean_entry(addr) ++ * ++ * Clean the specified entry of any caches such that the MMU ++ * translation fetches will obtain correct data. ++ * ++ * addr: cache-unaligned virtual address ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_dcache_clean_entry) ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ bic r0, r0, #DCACHELINESIZE - 1 ++ mcr p15, 0, r0, c7, c10, 1 @ clean single D entry ++#endif ++ mov r1, #0 ++ mcr p15, 0, r1, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ================================ I-CACHE =============================== */ ++ ++/* ++ * cpu_arm1022_icache_invalidate_range(start, end) ++ * ++ * invalidate a range of virtual addresses from the Icache ++ * ++ * This is a little misleading, it is not intended to clean out ++ * the i-cache but to make sure that any data written to the ++ * range is made consistent. This means that when we execute code ++ * in that region, everything works as we expect. ++ * ++ * This generally means writing back data in the Dcache and ++ * write buffer and invalidating the Icache over that region ++ * ++ * start: virtual start address ++ * end: virtual end address ++ * ++ * NOTE: ICACHELINESIZE == DCACHELINESIZE (so we don't need to ++ * loop twice, once for i-cache, once for d-cache) ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_icache_invalidate_range) ++ bic r0, r0, #ICACHELINESIZE - 1 ++ sub r3, r1, r0 ++ cmp r3, #MAX_AREA_SIZE ++ movhs r2, #1 ++ bhs cpu_arm1022_cache_clean_invalidate_all_r2 ++1: ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mcr p15, 0, r0, c7, c10, 1 @ Clean D entry ++#endif ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ mcr p15, 0, r0, c7, c5, 1 @ Invalidate I entry ++#endif ++ add r0, r0, #DCACHELINESIZE ++ cmp r0, r1 ++ bls 1b @ unsigned lower or same - includes r1 entry ++ ++ mov r0, #0 ++ mcr p15, 0, r0, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++ENTRY(cpu_arm1022_icache_invalidate_page) ++ mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page ++ mov r0, r0, LSL #LOG2PAGESIZE ++ add r1, r0, #PAGESIZE ++ b cpu_arm1022_icache_invalidate_range ++ ++/* ================================== TLB ================================= */ ++ ++/* ++ * cpu_arm1022_tlb_invalidate_all() ++ * ++ * Invalidate all TLB entries ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_tlb_invalidate_all) ++ mov r0, #0 ++ mcr p15, 0, r0, c7, c10, 4 @ drain WB ++ mcr p15, 0, r0, c8, c7, 0 @ invalidate I & D tlbs ++ mov pc, lr ++ ++/* ++ * cpu_arm1022_tlb_invalidate_range(start, end) ++ * ++ * invalidate TLB entries covering the specified range ++ * ++ * start: range start address ++ * end: range end address ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_tlb_invalidate_range) ++ sub r3, r1, r0 ++ cmp r3, #256 * PAGESIZE ++ bhs cpu_arm1022_tlb_invalidate_all ++ mov r3, #0 ++ mcr p15, 0, r3, c7, c10, 4 @ drain WB ++ mov r3, #PAGESIZE ++ sub r3, r3, #1 ++ bic r0, r0, r3 ++1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry ++ mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry ++ add r0, r0, #PAGESIZE ++ cmp r0, r1 ++ bls 1b ++ mov pc, lr ++ ++/* ++ * cpu_arm1022_tlb_invalidate_page(page, flags) ++ * ++ * invalidate the TLB entries for the specified page. ++ * ++ * page: page to invalidate ++ * flags: non-zero if we include the I TLB ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_tlb_invalidate_page) ++ mov r3, #0 ++ mcr p15, 0, r3, c7, c10, 4 @ drain WB ++ mov r0, r0, LSR #LOG2PAGESIZE @ round down to nearest page ++ mov r0, r0, LSL #LOG2PAGESIZE ++ teq r1, #0 ++ mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry ++ mcrne p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry ++ mov pc, lr ++ ++/* =============================== PageTable ============================== */ ++ ++/* ++ * cpu_arm1022_set_pgd(pgd) ++ * ++ * Set the translation base pointer to be as described by pgd. ++ * ++ * pgd: new page tables ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_set_pgd) ++ stmfd sp!, {lr} ++ bl cpu_arm1022_cache_clean_invalidate_all @ preserves r0 ++ mov r1, #0 ++ mcr p15, 0, r0, c2, c0, 0 @ load page table pointer ++ mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs ++ ldmfd sp!, {pc} ++ ++/* ++ * cpu_arm1022_set_pmd(pmdp, pmd) ++ * ++ * Set a level 1 translation table entry, and clean it out of ++ * any caches such that the MMUs can load it correctly. ++ * ++ * pmdp: pointer to PMD entry ++ * pmd: PMD value to store ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_set_pmd) ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ eor r2, r1, #0x0a @ C & Section ++ tst r2, #0x0b ++ biceq r1, r1, #4 @ clear bufferable bit ++#endif ++ str r1, [r0] ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mcr p15, 0, r0, c7, c10, 1 @ clean D entry ++#endif ++ mov r0, #0 ++ mcr p15, 0, r0, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++/* ++ * cpu_arm1022_set_pte(ptep, pte) ++ * ++ * Set a PTE and flush it out ++ */ ++ .align 5 ++ENTRY(cpu_arm1022_set_pte) ++ str r1, [r0], #-1024 @ linux version ++ ++ eor r1, r1, #LPTE_PRESENT | LPTE_YOUNG | LPTE_WRITE | LPTE_DIRTY ++ ++ bic r2, r1, #0xff0 ++ bic r2, r2, #3 ++ orr r2, r2, #HPTE_TYPE_SMALL ++ ++ tst r1, #LPTE_USER @ User? ++ orrne r2, r2, #HPTE_AP_READ ++ ++ tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? ++ orreq r2, r2, #HPTE_AP_WRITE ++ ++ tst r1, #LPTE_PRESENT | LPTE_YOUNG @ Present and Young? ++ movne r2, #0 ++ ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ eor r3, r2, #0x0a @ C and Small Page? ++ tst r3, #0x0b @ if so.. ++ biceq r2, r2, #0x04 @ clear the bufferable bit ++#endif ++ str r2, [r0] @ hardware version ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ mcr p15, 0, r0, c7, c10, 1 @ clean D entry ++#endif ++ mov r1, #0 ++ mcr p15, 0, r1, c7, c10, 4 @ drain WB ++ mov pc, lr ++ ++ ++cpu_manu_name: ++ .asciz "ARM" ++ENTRY(cpu_arm1022_name) ++ .ascii "Arm1022E" ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ .ascii "i" ++#endif ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ .ascii "d" ++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH ++ .ascii "(wt)" ++#endif ++#ifdef CONFIG_CPU_DCACHE_WRITEBACK ++ .ascii "(wb)" ++#endif ++#endif ++#ifndef CONFIG_CPU_BPREDICT_DISABLE ++ .ascii "B" ++#endif ++#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN ++ .ascii "RR" ++#endif ++ .ascii "\0" ++ .align ++ ++ .section ".text.init", #alloc, #execinstr ++ ++__arm1022_setup: ++ mov r0, #0 ++ mcr p15, 0, r0, c7, c7, 0 @ invalidate I,D caches on v4 ++ mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 ++ mcr p15, 0, r0, c8, c7, 0 @ invalidate I,D TLBs on v4 ++ mcr p15, 0, r4, c2, c0, 0 @ load page table pointer ++ mov r0, #0x1f @ Domains 0, 1 = client ++ mcr p15, 0, r0, c3, c0, 0 @ load domain access register ++ ++ mrc p15, 0, r0, c1, c0, 0 @ Read current control register ++/* ++ * The only thing worth keeping from the initial control register is the endian bit ++ */ ++ ++ and r0, r0, #0x0080 @ ........B....... Preserve endian bit, zero all others. ++ orr r0, r0, #0x0070 @ .........111.... Set the SBO (Should Be One) bits ++/* ++ * Turn on what we want. ++ */ ++ orr r0, r0, #0x0001 @ ...............M Enable MMU (Alignment is special cased elsewhere) ++ orr r0, r0, #0x0100 @ .......S........ Enable system MMU protection ++ orr r0, r0, #0x2000 @ ..V............. Enable high vectors ++ ++#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN ++ orr r0, r0, #0x4000 @ .R.............. Force round-robin replacement ++#endif ++ ++#ifndef CONFIG_CPU_BPREDICT_DISABLE ++ orr r0, r0, #0x0800 @ ....Z........... Enable branch prediction ++#endif ++ ++#ifdef CONFIG_CPU_DCACHE_ENABLE ++ orr r0, r0, #0x0004 @ .............C.. Enable D cache ++#endif ++#ifndef CONFIG_CPU_WB_DISABLE ++ orr r0, r0, #0x0008 @ ............W... Write Buffer enabled ++#endif ++ ++#ifdef CONFIG_CPU_ICACHE_ENABLE ++ orr r0, r0, #0x1000 @ ...I............ Enable I Cache ++#endif ++ ++ mov pc, lr ++ ++ .text ++ ++/* ++ * Purpose : Function pointers used to access above functions - all calls ++ * come through these ++ */ ++ .type arm1022_processor_functions, #object ++arm1022_processor_functions: ++ .word cpu_arm1022_data_abort ++ .word cpu_arm1022_check_bugs ++ .word cpu_arm1022_proc_init ++ .word cpu_arm1022_proc_fin ++ .word cpu_arm1022_reset ++ .word cpu_arm1022_do_idle ++ ++ /* cache */ ++ .word cpu_arm1022_cache_clean_invalidate_all ++ .word cpu_arm1022_cache_clean_invalidate_range ++ .word cpu_arm1022_flush_ram_page ++ ++ /* dcache */ ++ .word cpu_arm1022_dcache_invalidate_range ++ .word cpu_arm1022_dcache_clean_range ++ .word cpu_arm1022_dcache_clean_page ++ .word cpu_arm1022_dcache_clean_entry ++ ++ /* icache */ ++ .word cpu_arm1022_icache_invalidate_range ++ .word cpu_arm1022_icache_invalidate_page ++ ++ /* tlb */ ++ .word cpu_arm1022_tlb_invalidate_all ++ .word cpu_arm1022_tlb_invalidate_range ++ .word cpu_arm1022_tlb_invalidate_page ++ ++ /* pgtable */ ++ .word cpu_arm1022_set_pgd ++ .word cpu_arm1022_set_pmd ++ .word cpu_arm1022_set_pte ++ .size arm1022_processor_functions, . - arm1022_processor_functions ++ ++ .type cpu_arm1022_info, #object ++cpu_arm1022_info: ++ .long cpu_manu_name ++ .long cpu_arm1022_name ++ .size cpu_arm1022_info, . - cpu_arm1022_info ++ ++ .type cpu_arch_name, #object ++cpu_arch_name: ++ .asciz "armv5t" ++ .size cpu_arch_name, . - cpu_arch_name ++ ++ .type cpu_elf_name, #object ++cpu_elf_name: ++ .asciz "v5" ++ .size cpu_elf_name, . - cpu_elf_name ++ .align ++ ++ .section ".proc.info", #alloc, #execinstr ++ ++ .type __arm1022_proc_info,#object ++__arm1022_proc_info: ++ .long 0x4100a220 @ ARM 1022 ++ .long 0xff00fff0 ++ .long 0x00000c1e @ mmuflags ++ b __arm1022_setup ++ .long cpu_arch_name ++ .long cpu_elf_name ++ .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB ++ .long cpu_arm1022_info ++ .long arm1022_processor_functions ++ .size __arm1022_proc_info, . - __arm1022_proc_info +diff -urN linux-2.4.26/arch/arm/mm/proc-arm1026.S linux-2.4.26-vrs1/arch/arm/mm/proc-arm1026.S +--- linux-2.4.26/arch/arm/mm/proc-arm1026.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/proc-arm1026.S 2004-02-10 23:03:10.000000000 +0000 +@@ -66,19 +66,24 @@ + * + * Returns: + * r0 = address of abort +- * r1 != 0 if writing +- * r3 = FSR ++ * r1 = FSR, bit 11 set if writing ++ * r3 = corrupted + * r4 = corrupted + */ + .align 5 + ENTRY(cpu_arm1026_data_abort) +- mrc p15, 0, r3, c5, c0, 0 @ get FSR +- and r2, r3, #0b1101 @ Check for translation error +- sub r1, r2, #0b0101 +- +- and r3, r3, #255 ++ mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR +- ++ bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR ++ tst r3, #PSR_J_BIT @ Java? ++ orrne r1, r1, #1 << 11 @ always assume write ++ movne pc, lr ++ tst r3, #PSR_T_BIT ++ ldrneh r3, [r2] @ read aborted thumb instruction ++ ldreq r3, [r2] @ read aborted ARM instruction ++ movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 ++ tst r3, #1 << 20 @ check write ++ orreq r1, r1, #1 << 11 + mov pc, lr + + /* +@@ -254,7 +259,7 @@ + */ + .align 5 + ENTRY(cpu_arm1026_dcache_invalidate_range) +-#ifndef CONFIG_CPU_ARM1026_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + tst r0, #DCACHELINESIZE - 1 + mcrne p15, 0, r0, c7, c10, 1 @ clean D entry + tst r1, #DCACHELINESIZE - 1 +@@ -279,7 +284,7 @@ + */ + .align 5 + ENTRY(cpu_arm1026_dcache_clean_range) +-#ifndef CONFIG_CPU_ARM1026_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + bic r0, r0, #DCACHELINESIZE - 1 + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE +@@ -309,7 +314,7 @@ + */ + .align 5 + ENTRY(cpu_arm1026_dcache_clean_page) +-#ifndef CONFIG_CPU_ARM1026_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + mov r1, #PAGESIZE + 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry + add r0, r0, #DCACHELINESIZE +@@ -330,7 +335,7 @@ + */ + .align 5 + ENTRY(cpu_arm1026_dcache_clean_entry) +-#ifndef CONFIG_CPU_ARM1026_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c10, 1 @ clean D entry + #endif + mcr p15, 0, r0, c7, c10, 4 @ drain WB +@@ -473,7 +478,7 @@ + biceq r1, r1, #4 @ clear bufferable bit + #endif + str r1, [r0] +-#ifndef CONFIG_CPU_ARM1026_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c10, 1 @ clean D entry + #endif + mcr p15, 0, r0, c7, c10, 4 @ drain WB +@@ -494,7 +499,7 @@ + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + +- tst r1, #LPTE_USER | LPTE_EXEC @ User or Exec? ++ tst r1, #LPTE_USER @ User? + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? +@@ -634,12 +639,12 @@ + + .type cpu_arch_name, #object + cpu_arch_name: +- .asciz "armv5EJ" ++ .asciz "armv5tej" + .size cpu_arch_name, . - cpu_arch_name + + .type cpu_elf_name, #object + cpu_elf_name: +- .asciz "v5EJ" ++ .asciz "v5" + .size cpu_elf_name, . - cpu_elf_name + .align + +diff -urN linux-2.4.26/arch/arm/mm/proc-arm6,7.S linux-2.4.26-vrs1/arch/arm/mm/proc-arm6,7.S +--- linux-2.4.26/arch/arm/mm/proc-arm6,7.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/proc-arm6,7.S 2004-01-14 21:38:43.000000000 +0000 +@@ -72,7 +72,7 @@ + 1: mcr p15, 0, r0, c6, c0, 0 @ purge TLB + add r0, r0, #4096 + cmp r0, r1 +- blt 1b ++ blo 1b + mov pc, lr + + ENTRY(cpu_arm7_tlb_invalidate_range) +@@ -85,7 +85,7 @@ + 1: mcr p15, 0, r0, c6, c0, 0 @ purge TLB + add r0, r0, #0x4000 + cmp r0, r1 +- blt 1b ++ blo 1b + mov pc, lr + #endif + +@@ -110,15 +110,13 @@ + * Purpose : obtain information about current aborted instruction + * + * Returns : r0 = address of abort +- * : r1 != 0 if writing +- * : r3 = FSR ++ * : r1 = FSR, bit 11 set if writing ++ * : r3 = corrupted + * : sp = pointer to registers + */ + + ENTRY(cpu_arm6_data_abort) + ldr r4, [r0] @ read instruction causing problem +- tst r4, r4, lsr #21 @ C = bit 20 +- sbc r1, r1, r1 @ r1 = C - 1 + and r2, r4, #14 << 24 + teq r2, #8 << 24 @ was it ldm/stm + bne Ldata_simple +@@ -144,14 +142,14 @@ + addeq r7, r0, r7, lsl #2 @ Do correction (signed) + Ldata_saver7: str r7, [sp, r5, lsr #14] @ Put register + Ldata_simple: mrc p15, 0, r0, c6, c0, 0 @ get FAR +- mrc p15, 0, r3, c5, c0, 0 @ get FSR +- and r3, r3, #255 ++ mrc p15, 0, r1, c5, c0, 0 @ get FSR ++ bic r1, r1, #1 << 11 | 1 << 10 ++ tst r4, #1 << 20 ++ orreq r1, r1, #1 << 11 + mov pc, lr + + ENTRY(cpu_arm7_data_abort) + ldr r4, [r0] @ read instruction causing problem +- tst r4, r4, lsr #21 @ C = bit 20 +- sbc r1, r1, r1 @ r1 = C - 1 + and r2, r4, #15 << 24 + add pc, pc, r2, lsr #22 @ Now branch to the relevent processing routine + movs pc, lr +@@ -336,7 +334,7 @@ + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + +- tst r1, #LPTE_USER | LPTE_EXEC @ User or Exec? ++ tst r1, #LPTE_USER @ User? + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? +diff -urN linux-2.4.26/arch/arm/mm/proc-arm720.S linux-2.4.26-vrs1/arch/arm/mm/proc-arm720.S +--- linux-2.4.26/arch/arm/mm/proc-arm720.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/proc-arm720.S 2004-02-24 09:30:43.000000000 +0000 +@@ -97,7 +97,7 @@ + 1: mcr p15, 0, r0, c8, c7, 1 @ flush TLB (v4) + add r0, r0, #PAGESIZE + cmp r0, r1 +- blt 1b ++ blo 1b + mov pc, lr + + /* +@@ -124,8 +124,8 @@ + * picture. Unfortunately, this does happen. We live with it. + * + * Returns : r0 = address of abort +- * : r1 != 0 if writing +- * : r3 = FSR ++ * : r1 = FSR, bit 11 set if writing ++ * : r3 = corrupted + * : sp = pointer to registers + */ + +@@ -150,16 +150,16 @@ + addeq r7, r0, r7, lsl #2 @ Do correction (signed) + Ldata_saver7: str r7, [sp, r5, lsr #14] @ Put register + Ldata_simple: mrc p15, 0, r0, c6, c0, 0 @ get FAR +- mrc p15, 0, r3, c5, c0, 0 @ get FSR +- and r3, r3, #255 ++ mrc p15, 0, r1, c5, c0, 0 @ get FSR ++ bic r1, r1, #1 << 11 | 1 << 10 ++ tst r4, #1 << 20 ++ orreq r1, r1, #1 << 11 + mov pc, lr + + ENTRY(cpu_arm720_data_abort) +- tst r3, #T_BIT ++ tst r3, #PSR_T_BIT + bne .data_thumb_abort +- ldr r4, [r0] @ read instruction causing problem +- tst r4, r4, lsr #21 @ C = bit 20 +- sbc r1, r1, r1 @ r1 = C - 1 ++ ldr r4, [r2] @ read instruction causing problem + and r2, r4, #15 << 24 + add pc, pc, r2, lsr #22 @ Now branch to the relevent processing routine + movs pc, lr +@@ -270,9 +270,9 @@ + b Ldata_saver7 + + .data_thumb_abort: +- ldrh r4, [r0] @ read instruction +- tst r4, r4, lsr #12 @ C = bit 11 +- sbc r1, r1, r1 @ r1 = C - 1 ++ ldrh r4, [r2] @ read instruction ++ tst r4, #1 << 11 ++ orrne r4, r4, #1 << 20 + and r2, r4, #15 << 12 + add pc, pc, r2, lsr #10 @ lookup in table + nop +@@ -318,8 +318,8 @@ + and r0, r0, #15 @ number of regs to transfer + ldr r7, [sp, #13 << 2] + tst r4, #1 << 11 +- addne r7, r7, r0, lsl #2 @ increment SP if PUSH +- subeq r7, r7, r0, lsr #2 @ decrement SP if POP ++ addeq r7, r7, r0, lsl #2 @ increment SP if PUSH ++ subne r7, r7, r0, lsl #2 @ decrement SP if POP + str r7, [sp, #13 << 2] + b Ldata_simple + +@@ -336,7 +336,7 @@ + and r0, r0, #15 @ number of regs to transfer + and r5, r4, #7 << 8 + ldr r7, [sp, r5, lsr #6] +- sub r7, r7, r0, lsr #2 @ always decrement ++ sub r7, r7, r0, lsl #2 @ always decrement + str r7, [sp, r5, lsr #6] + b Ldata_simple + +@@ -418,7 +418,7 @@ + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + +- tst r1, #LPTE_USER | LPTE_EXEC @ User or Exec? ++ tst r1, #LPTE_USER @ User? + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? +diff -urN linux-2.4.26/arch/arm/mm/proc-arm920.S linux-2.4.26-vrs1/arch/arm/mm/proc-arm920.S +--- linux-2.4.26/arch/arm/mm/proc-arm920.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/proc-arm920.S 2004-01-14 21:32:24.000000000 +0000 +@@ -71,12 +71,16 @@ + */ + .align 5 + ENTRY(cpu_arm920_data_abort) +- mrc p15, 0, r3, c5, c0, 0 @ get FSR ++ mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR +- ldr r1, [r2] @ read aborted instruction +- and r3, r3, #255 +- tst r1, r1, lsr #21 @ C = bit 20 +- sbc r1, r1, r1 @ r1 = C - 1 ++ ++ tst r3, #PSR_T_BIT ++ ldrneh r3, [r2] @ read aborted thumb instruction ++ ldreq r3, [r2] @ read aborted ARM instruction ++ bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR ++ movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 ++ tst r3, #1 << 20 @ check write ++ orreq r1, r1, #1 << 11 + mov pc, lr + + /* +@@ -186,10 +190,9 @@ + .align 5 + ENTRY(cpu_arm920_cache_clean_invalidate_range) + bic r0, r0, #DCACHELINESIZE - 1 @ && added by PGM +- bic r1, r1, #DCACHELINESIZE - 1 @ && added by DHM + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE +- bgt cpu_arm920_cache_clean_invalidate_all_r2 ++ bhi cpu_arm920_cache_clean_invalidate_all_r2 + 1: teq r2, #0 + #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry +@@ -207,7 +210,7 @@ + add r0, r0, #DCACHELINESIZE + #endif + cmp r0, r1 +- blt 1b ++ blo 1b + + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr +@@ -253,18 +256,17 @@ + */ + .align 5 + ENTRY(cpu_arm920_dcache_invalidate_range) +-#ifndef CONFIG_CPU_ARM920_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + tst r0, #DCACHELINESIZE - 1 + mcrne p15, 0, r0, c7, c10, 1 @ clean D entry + tst r1, #DCACHELINESIZE - 1 + mcrne p15, 0, r1, c7, c10, 1 @ clean D entry + #endif @ clean D entry + bic r0, r0, #DCACHELINESIZE - 1 +- bic r1, r1, #DCACHELINESIZE - 1 + 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry + add r0, r0, #DCACHELINESIZE + cmp r0, r1 +- blt 1b ++ blo 1b + mov pc, lr + + /* +@@ -279,20 +281,17 @@ + */ + .align 5 + ENTRY(cpu_arm920_dcache_clean_range) +-#ifndef CONFIG_CPU_ARM920_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + bic r0, r0, #DCACHELINESIZE - 1 + sub r1, r1, r0 + cmp r1, #MAX_AREA_SIZE + mov r2, #0 +- bgt cpu_arm920_cache_clean_invalidate_all_r2 +- +- bic r1, r1, #DCACHELINESIZE -1 +- add r1, r1, #DCACHELINESIZE ++ bhi cpu_arm920_cache_clean_invalidate_all_r2 + + 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry + add r0, r0, #DCACHELINESIZE + subs r1, r1, #DCACHELINESIZE +- bpl 1b ++ bcs 1b + #endif + mcr p15, 0, r2, c7, c10, 4 @ drain WB + mov pc, lr +@@ -312,7 +311,7 @@ + */ + .align 5 + ENTRY(cpu_arm920_dcache_clean_page) +-#ifndef CONFIG_CPU_ARM920_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + mov r1, #PAGESIZE + 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry + add r0, r0, #DCACHELINESIZE +@@ -333,7 +332,7 @@ + */ + .align 5 + ENTRY(cpu_arm920_dcache_clean_entry) +-#ifndef CONFIG_CPU_ARM920_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c10, 1 @ clean D entry + #endif + mcr p15, 0, r0, c7, c10, 4 @ drain WB +@@ -365,16 +364,13 @@ + bic r0, r0, #ICACHELINESIZE - 1 @ Safety check + sub r1, r1, r0 + cmp r1, #MAX_AREA_SIZE +- bgt cpu_arm920_cache_clean_invalidate_all_r2 +- +- bic r1, r1, #ICACHELINESIZE - 1 +- add r1, r1, #ICACHELINESIZE ++ bhi cpu_arm920_cache_clean_invalidate_all_r2 + + 1: mcr p15, 0, r0, c7, c5, 1 @ Clean I entry + mcr p15, 0, r0, c7, c10, 1 @ Clean D entry + add r0, r0, #ICACHELINESIZE + subs r1, r1, #ICACHELINESIZE +- bne 1b ++ bcs 1b + + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ drain WB +@@ -418,13 +414,12 @@ + mov r3, #PAGESIZE + sub r3, r3, #1 + bic r0, r0, r3 +- bic r1, r1, r3 + + 1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry + mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry + add r0, r0, #PAGESIZE + cmp r0, r1 +- blt 1b ++ blo 1b + mov pc, lr + + /* +@@ -457,7 +452,6 @@ + ENTRY(cpu_arm920_set_pgd) + mov ip, #0 + #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH +- /* Any reason why we don't use mcr p15, 0, r0, c7, c7, 0 here? --rmk */ + mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache + #else + @ && 'Clean & Invalidate whole DCache' +@@ -514,7 +508,7 @@ + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + +- tst r1, #LPTE_USER | LPTE_EXEC @ User or Exec? ++ tst r1, #LPTE_USER @ User? + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? +diff -urN linux-2.4.26/arch/arm/mm/proc-arm922.S linux-2.4.26-vrs1/arch/arm/mm/proc-arm922.S +--- linux-2.4.26/arch/arm/mm/proc-arm922.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/proc-arm922.S 2004-01-14 21:32:24.000000000 +0000 +@@ -62,17 +62,20 @@ + * + * Returns: + * r0 = address of abort +- * r1 != 0 if writing +- * r3 = FSR ++ * r1 = FSR, bit 11 set if writing ++ * r3 = corrupted + */ + .align 5 + ENTRY(cpu_arm922_data_abort) +- ldr r1, [r0] @ read aborted instruction ++ mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR +- tst r1, r1, lsr #21 @ C = bit 20 +- mrc p15, 0, r3, c5, c0, 0 @ get FSR +- sbc r1, r1, r1 @ r1 = C - 1 +- and r3, r3, #255 ++ tst r3, #PSR_T_BIT ++ ldrneh r3, [r2] @ read aborted thumb instruction ++ ldreq r3, [r2] @ read aborted ARM instruction ++ bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR ++ movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 ++ tst r3, #1 << 20 @ check write ++ orreq r1, r1, #1 << 11 + mov pc, lr + + /* +@@ -185,7 +188,7 @@ + bic r1, r1, #DCACHELINESIZE - 1 @ && added by DHM + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE +- bgt cpu_arm922_cache_clean_invalidate_all_r2 ++ bhi cpu_arm922_cache_clean_invalidate_all_r2 + 1: teq r2, #0 + #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry +@@ -203,7 +206,7 @@ + add r0, r0, #DCACHELINESIZE + #endif + cmp r0, r1 +- blt 1b ++ blo 1b + + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr +@@ -249,7 +252,7 @@ + */ + .align 5 + ENTRY(cpu_arm922_dcache_invalidate_range) +-#ifndef CONFIG_CPU_ARM922_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + tst r0, #DCACHELINESIZE - 1 + mcrne p15, 0, r0, c7, c10, 1 @ clean D entry + tst r1, #DCACHELINESIZE - 1 +@@ -260,7 +263,7 @@ + 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry + add r0, r0, #DCACHELINESIZE + cmp r0, r1 +- blt 1b ++ blo 1b + mov pc, lr + + /* +@@ -275,12 +278,12 @@ + */ + .align 5 + ENTRY(cpu_arm922_dcache_clean_range) +-#ifndef CONFIG_CPU_ARM922_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + bic r0, r0, #DCACHELINESIZE - 1 + sub r1, r1, r0 + cmp r1, #MAX_AREA_SIZE + mov r2, #0 +- bgt cpu_arm922_cache_clean_invalidate_all_r2 ++ bhi cpu_arm922_cache_clean_invalidate_all_r2 + + bic r1, r1, #DCACHELINESIZE -1 + add r1, r1, #DCACHELINESIZE +@@ -308,7 +311,7 @@ + */ + .align 5 + ENTRY(cpu_arm922_dcache_clean_page) +-#ifndef CONFIG_CPU_ARM922_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + mov r1, #PAGESIZE + 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry + add r0, r0, #DCACHELINESIZE +@@ -329,7 +332,7 @@ + */ + .align 5 + ENTRY(cpu_arm922_dcache_clean_entry) +-#ifndef CONFIG_CPU_ARM922_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c10, 1 @ clean D entry + #endif + mcr p15, 0, r0, c7, c10, 4 @ drain WB +@@ -361,7 +364,7 @@ + bic r0, r0, #ICACHELINESIZE - 1 @ Safety check + sub r1, r1, r0 + cmp r1, #MAX_AREA_SIZE +- bgt cpu_arm922_cache_clean_invalidate_all_r2 ++ bhi cpu_arm922_cache_clean_invalidate_all_r2 + + bic r1, r1, #ICACHELINESIZE - 1 + add r1, r1, #ICACHELINESIZE +@@ -420,7 +423,7 @@ + mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry + add r0, r0, #PAGESIZE + cmp r0, r1 +- blt 1b ++ blo 1b + mov pc, lr + + /* +@@ -510,7 +513,7 @@ + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + +- tst r1, #LPTE_USER | LPTE_EXEC @ User or Exec? ++ tst r1, #LPTE_USER @ User? + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? +diff -urN linux-2.4.26/arch/arm/mm/proc-arm925.S linux-2.4.26-vrs1/arch/arm/mm/proc-arm925.S +--- linux-2.4.26/arch/arm/mm/proc-arm925.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/proc-arm925.S 2004-01-14 21:32:24.000000000 +0000 +@@ -69,24 +69,24 @@ + * + * Returns: + * r0 = address of abort +- * r1 != 0 if writing +- * r3 = FSR ++ * r1 = FSR, bit 11 set if writing ++ * r3 = corrupted + * r4 = corrupted + */ + .align 5 + ENTRY(cpu_arm925_data_abort) ++ mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR +- mrc p15, 0, r4, c5, c0, 0 @ get FSR +- +- tst r3, #1<<5 @ Check for Thumb-bit (NE -> found) +- ldrneh r1, [r2] @ Read aborted Thumb instruction +- tstne r1, r1, lsr #12 @ C = bit 11 +- +- ldreq r1, [r2] @ Read aborted ARM instruction +- tsteq r1, r1, lsr #21 @ C = bit 20 +- +- sbc r1, r1, r1 @ r1 = C - 1 +- and r3, r4, #255 ++ bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR ++ tst r3, #PSR_J_BIT @ Java? ++ orrne r1, r1, #1 << 11 @ always assume write ++ movne pc, lr ++ tst r3, #PSR_T_BIT @ Thumb? ++ ldrneh r3, [r2] @ read aborted thumb instruction ++ ldreq r3, [r2] @ read aborted ARM instruction ++ movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 ++ tst r3, #1 << 20 @ L = 0 -> write ++ orreq r1, r1, #1 << 11 @ yes. + mov pc, lr + + /* +@@ -207,7 +207,7 @@ + bic r1, r1, #DCACHELINESIZE - 1 @ && added by DHM + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE +- bgt cpu_arm925_cache_clean_invalidate_all_r2 ++ bhi cpu_arm925_cache_clean_invalidate_all_r2 + 1: teq r2, #0 + #ifdef CONFIG_CPU_ARM925_WRITETHROUGH + mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry +@@ -225,7 +225,7 @@ + add r0, r0, #DCACHELINESIZE + #endif + cmp r0, r1 +- blt 1b ++ blo 1b + + mcr p15, 0, r1, c7, c10, 4 @ drain WB + mov pc, lr +@@ -282,7 +282,7 @@ + 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry + add r0, r0, #DCACHELINESIZE + cmp r0, r1 +- blt 1b ++ blo 1b + mov pc, lr + + /* +@@ -302,7 +302,7 @@ + sub r1, r1, r0 + cmp r1, #MAX_AREA_SIZE + mov r2, #0 +- bgt cpu_arm925_cache_clean_invalidate_all_r2 ++ bhi cpu_arm925_cache_clean_invalidate_all_r2 + + bic r1, r1, #DCACHELINESIZE -1 + add r1, r1, #DCACHELINESIZE +@@ -383,7 +383,7 @@ + bic r0, r0, #ICACHELINESIZE - 1 @ Safety check + sub r1, r1, r0 + cmp r1, #MAX_AREA_SIZE +- bgt cpu_arm925_cache_clean_invalidate_all_r2 ++ bhi cpu_arm925_cache_clean_invalidate_all_r2 + + bic r1, r1, #ICACHELINESIZE - 1 + add r1, r1, #ICACHELINESIZE +@@ -443,7 +443,7 @@ + mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry + add r0, r0, #PAGESIZE + cmp r0, r1 +- blt 1b ++ blo 1b + mov pc, lr + + /* +@@ -532,7 +532,7 @@ + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + +- tst r1, #LPTE_USER | LPTE_EXEC @ User or Exec? ++ tst r1, #LPTE_USER @ User? + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? +diff -urN linux-2.4.26/arch/arm/mm/proc-arm926.S linux-2.4.26-vrs1/arch/arm/mm/proc-arm926.S +--- linux-2.4.26/arch/arm/mm/proc-arm926.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/proc-arm926.S 2004-01-14 21:38:43.000000000 +0000 +@@ -66,28 +66,24 @@ + * + * Returns: + * r0 = address of abort +- * r1 != 0 if writing +- * r3 = FSR ++ * r1 = FSR, bit 11 set if writing ++ * r3 = corrupted + * r4 = corrupted + */ + .align 5 + ENTRY(cpu_arm926_data_abort) ++ mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR +- mrc p15, 0, r4, c5, c0, 0 @ get FSR +- +- tst r3, #1<<24 @ Check for Jbit (NE -> found) +- movne r3, #-1 @ Mark as writing +- bne 2f +- +- tst r3, #1<<5 @ Check for Thumb-bit (NE -> found) +- ldrneh r1, [r2] @ Read aborted Thumb instruction +- ldreq r1, [r2] @ Read aborted ARM instruction +- movne r1, r1, lsl #(20-12) @ shift thumb bit 10 to ARM bit 20 +- tsteq r1, r1, lsr #21 @ C = bit 20 +- +- sbc r1, r1, r1 @ r1 = C - 1 +-2: +- and r3, r4, #255 ++ bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR ++ tst r3, #PSR_J_BIT @ Java? ++ orrne r1, r1, #1 << 11 @ always assume write ++ movne pc, lr ++ tst r3, #PSR_T_BIT @ Thumb? ++ ldrneh r3, [r2] @ read aborted thumb instruction ++ ldreq r3, [r2] @ read aborted ARM instruction ++ movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 ++ tst r3, #1 << 20 @ L = 0 -> write ++ orreq r1, r1, #1 << 11 @ yes. + mov pc, lr + + /* +@@ -263,7 +259,7 @@ + */ + .align 5 + ENTRY(cpu_arm926_dcache_invalidate_range) +-#ifndef CONFIG_CPU_ARM926_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + tst r0, #DCACHELINESIZE - 1 + mcrne p15, 0, r0, c7, c10, 1 @ clean D entry + tst r1, #DCACHELINESIZE - 1 +@@ -288,7 +284,7 @@ + */ + .align 5 + ENTRY(cpu_arm926_dcache_clean_range) +-#ifndef CONFIG_CPU_ARM926_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + bic r0, r0, #DCACHELINESIZE - 1 + sub r3, r1, r0 + cmp r3, #MAX_AREA_SIZE +@@ -318,7 +314,7 @@ + */ + .align 5 + ENTRY(cpu_arm926_dcache_clean_page) +-#ifndef CONFIG_CPU_ARM926_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + mov r1, #PAGESIZE + 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry + add r0, r0, #DCACHELINESIZE +@@ -339,7 +335,7 @@ + */ + .align 5 + ENTRY(cpu_arm926_dcache_clean_entry) +-#ifndef CONFIG_CPU_ARM926_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c10, 1 @ clean D entry + #endif + mcr p15, 0, r0, c7, c10, 4 @ drain WB +@@ -482,7 +478,7 @@ + biceq r1, r1, #4 @ clear bufferable bit + #endif + str r1, [r0] +-#ifndef CONFIG_CPU_ARM926_WRITETHROUGH ++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c10, 1 @ clean D entry + #endif + mcr p15, 0, r0, c7, c10, 4 @ drain WB +@@ -503,7 +499,7 @@ + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + +- tst r1, #LPTE_USER | LPTE_EXEC @ User or Exec? ++ tst r1, #LPTE_USER @ User? + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? +diff -urN linux-2.4.26/arch/arm/mm/proc-sa110.S linux-2.4.26-vrs1/arch/arm/mm/proc-sa110.S +--- linux-2.4.26/arch/arm/mm/proc-sa110.S 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/mm/proc-sa110.S 2004-01-14 21:32:24.000000000 +0000 +@@ -86,12 +86,12 @@ + .align 5 + ENTRY(cpu_sa110_data_abort) + ENTRY(cpu_sa1100_data_abort) +- mrc p15, 0, r3, c5, c0, 0 @ get FSR ++ mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR +- ldr r1, [r2] @ read aborted instruction +- and r3, r3, #255 +- tst r1, r1, lsr #21 @ C = bit 20 +- sbc r1, r1, r1 @ r1 = C - 1 ++ ldr r3, [r2] @ read aborted instruction ++ bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR ++ tst r3, #1 << 20 @ check write ++ orreq r1, r1, #1 << 11 + mov pc, lr + + /* +@@ -551,7 +551,7 @@ + bic r2, r2, #3 + orr r2, r2, #HPTE_TYPE_SMALL + +- tst r1, #LPTE_USER | LPTE_EXEC @ User or Exec? ++ tst r1, #LPTE_USER @ User? + orrne r2, r2, #HPTE_AP_READ + + tst r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty? +diff -urN linux-2.4.26/arch/arm/tools/mach-types linux-2.4.26-vrs1/arch/arm/tools/mach-types +--- linux-2.4.26/arch/arm/tools/mach-types 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/arm/tools/mach-types 2004-04-19 21:11:38.000000000 +0100 +@@ -6,7 +6,7 @@ + # To add an entry into this database, please see Documentation/arm/README, + # or contact rmk@arm.linux.org.uk + # +-# Last update: Sat Jun 28 12:10:54 2003 ++# Last update: Mon Apr 19 21:11:35 2004 + # + # machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number + # +@@ -202,7 +202,7 @@ + fester SA1100_FESTER FESTER 191 + gpi ARCH_GPI GPI 192 + smdk2410 ARCH_SMDK2410 SMDK2410 193 +-premium ARCH_PREMIUM PREMIUM 194 ++i519 ARCH_I519 I519 194 + nexio SA1100_NEXIO NEXIO 195 + bitbox SA1100_BITBOX BITBOX 196 + g200 SA1100_G200 G200 197 +@@ -228,7 +228,7 @@ + arnold SA1100_ARNOLD ARNOLD 217 + psiboard SA1100_PSIBOARD PSIBOARD 218 + jz8028 ARCH_JZ8028 JZ8028 219 +-h5400 ARCH_IPAQ3 IPAQ3 220 ++h5400 ARCH_H5400 H5400 220 + forte SA1100_FORTE FORTE 221 + acam SA1100_ACAM ACAM 222 + abox SA1100_ABOX ABOX 223 +@@ -259,7 +259,7 @@ + stork_egg ARCH_STORK_EGG STORK_EGG 248 + wismo SA1100_WISMO WISMO 249 + ezlinx ARCH_EZLINX EZLINX 250 +-at91rm9200 ARCH_AT91 AT91 251 ++at91rm9200 ARCH_AT91RM9200 AT91RM9200 251 + orion ARCH_ORION ORION 252 + neptune ARCH_NEPTUNE NEPTUNE 253 + hackkit SA1100_HACKKIT HACKKIT 254 +@@ -295,12 +295,12 @@ + adsbitsyplus SA1100_ADSBITSYPLUS ADSBITSYPLUS 284 + adsagc SA1100_ADSAGC ADSAGC 285 + stp7312 ARCH_STP7312 STP7312 286 +-nx_phnx ARCH_PXA255 PXA255 287 ++nx_phnx MACH_NX_PHNX NX_PHNX 287 + wep_ep250 ARCH_WEP_EP250 WEP_EP250 288 + inhandelf3 ARCH_INHANDELF3 INHANDELF3 289 + adi_coyote ARCH_ADI_COYOTE ADI_COYOTE 290 + iyonix ARCH_IYONIX IYONIX 291 +-damicam_sa1110 ARCH_DAMICAM_SA1110 DAMICAM_SA1110 292 ++damicam1 ARCH_DAMICAM_SA1110 DAMICAM_SA1110 292 + meg03 ARCH_MEG03 MEG03 293 + pxa_whitechapel ARCH_PXA_WHITECHAPEL PXA_WHITECHAPEL 294 + nwsc ARCH_NWSC NWSC 295 +@@ -356,3 +356,172 @@ + seedpxa_c2 ARCH_SEEDPXA_C2 SEEDPXA_C2 345 + ixp4xx_mguardpci ARCH_IXP4XX_MGUARD_PCI IXP4XX_MGUARD_PCI 346 + h1940 ARCH_H1940 H1940 347 ++scorpio ARCH_SCORPIO SCORPIO 348 ++viva ARCH_VIVA VIVA 349 ++pxa_xcard ARCH_PXA_XCARD PXA_XCARD 350 ++csb335 ARCH_CSB335 CSB335 351 ++ixrd425 ARCH_IXRD425 IXRD425 352 ++iq80315 ARCH_IQ80315 IQ80315 353 ++nmp7312 ARCH_NMP7312 NMP7312 354 ++cx861xx ARCH_CX861XX CX861XX 355 ++enp2611 ARCH_ENP2611 ENP2611 356 ++xda SA1100_XDA XDA 357 ++csir_ims ARCH_CSIR_IMS CSIR_IMS 358 ++ixp421_dnaeeth ARCH_IXP421_DNAEETH IXP421_DNAEETH 359 ++pocketserv9200 ARCH_POCKETSERV9200 POCKETSERV9200 360 ++toto ARCH_TOTO TOTO 361 ++s3c2440 ARCH_S3C2440 S3C2440 362 ++ks8695p ARCH_KS8695P KS8695P 363 ++se4000 ARCH_SE4000 SE4000 364 ++quadriceps ARCH_QUADRICEPS QUADRICEPS 365 ++bronco ARCH_BRONCO BRONCO 366 ++esl_wireless_tab ARCH_ESL_WIRELESS_TABLETESL_WIRELESS_TABLET 367 ++esl_sofcomp ARCH_ESL_SOFCOMP ESL_SOFCOMP 368 ++s5c7375 ARCH_S5C7375 S5C7375 369 ++spearhead ARCH_SPEARHEAD SPEARHEAD 370 ++pantera ARCH_PANTERA PANTERA 371 ++prayoglite ARCH_PRAYOGLITE PRAYOGLITE 372 ++gumstik ARCH_GUMSTIK GUMSTIK 373 ++rcube ARCH_RCUBE RCUBE 374 ++rea_olv ARCH_REA_OLV REA_OLV 375 ++pxa_iphone ARCH_PXA_IPHONE PXA_IPHONE 376 ++s3c3410 ARCH_S3C3410 S3C3410 377 ++espd_4510b ARCH_ESPD_4510B ESPD_4510B 378 ++mp1x ARCH_MP1X MP1X 379 ++at91rm9200tb ARCH_AT91RM9200TB AT91RM9200TB 380 ++adsvgx ARCH_ADSVGX ADSVGX 381 ++omap_h2 ARCH_OMAP_H2 OMAP_H2 382 ++pelee ARCH_PELEE PELEE 383 ++e740 MACH_E740 E740 384 ++iq80331 ARCH_IQ80331 IQ80331 385 ++versatile_pb ARCH_VERSATILE_PB VERSATILE_PB 387 ++kev7a400 MACH_KEV7A400 KEV7A400 388 ++lpd7a400 MACH_LPD7A400 LPD7A400 389 ++lpd7a404 MACH_LPD7A404 LPD7A404 390 ++fujitsu_camelot ARCH_FUJITSU_CAMELOT FUJITSU_CAMELOT 391 ++janus2m ARCH_JANUS2M JANUS2M 392 ++embtf MACH_EMBTF EMBTF 393 ++hpm MACH_HPM HPM 394 ++smdk2410tk MACH_SMDK2410TK SMDK2410TK 395 ++smdk2410aj MACH_SMDK2410AJ SMDK2410AJ 396 ++streetracer MACH_STREETRACER STREETRACER 397 ++eframe MACH_EFRAME EFRAME 398 ++csb337 MACH_CSB337 CSB337 399 ++pxa_lark MACH_PXA_LARK PXA_LARK 400 ++pxa_pnp2110 MACH_PNP2110 PNP2110 401 ++tcc72x MACH_TCC72X TCC72X 402 ++altair MACH_ALTAIR ALTAIR 403 ++kc3 MACH_KC3 KC3 404 ++sinteftd MACH_SINTEFTD SINTEFTD 405 ++mainstone MACH_MAINSTONE MAINSTONE 406 ++aday4x MACH_ADAY4X ADAY4X 407 ++lite300 MACH_LITE300 LITE300 408 ++s5c7376 MACH_S5C7376 S5C7376 409 ++mt02 MACH_MT02 MT02 410 ++mport3s MACH_MPORT3S MPORT3S 411 ++ra_alpha MACH_RA_ALPHA RA_ALPHA 412 ++xcep MACH_XCEP XCEP 413 ++arcom_mercury MACH_ARCOM_MERCURY ARCOM_MERCURY 414 ++stargate MACH_STARGATE STARGATE 415 ++armadilloj MACH_ARMADILLOJ ARMADILLOJ 416 ++elroy_jack MACH_ELROY_JACK ELROY_JACK 417 ++backend MACH_BACKEND BACKEND 418 ++s5linbox MACH_S5LINBOX S5LINBOX 419 ++nomadik MACH_NOMADIK NOMADIK 420 ++ia_cpu_9200 MACH_IA_CPU_9200 IA_CPU_9200 421 ++at91_bja1 MACH_AT91_BJA1 AT91_BJA1 422 ++corgi MACH_CORGI CORGI 423 ++poodle MACH_POODLE POODLE 424 ++ten MACH_TEN TEN 425 ++roverp5p MACH_ROVERP5P ROVERP5P 426 ++sc2700 MACH_SC2700 SC2700 427 ++ex_eagle MACH_EX_EAGLE EX_EAGLE 428 ++nx_pxa12 MACH_NX_PXA12 NX_PXA12 429 ++nx_pxa5 MACH_NX_PXA5 NX_PXA5 430 ++blackboard2 MACH_BLACKBOARD2 BLACKBOARD2 431 ++i819 MACH_I819 I819 432 ++ixmb995e MACH_IXMB995E IXMB995E 433 ++skyrider MACH_SKYRIDER SKYRIDER 434 ++skyhawk MACH_SKYHAWK SKYHAWK 435 ++enterprise MACH_ENTERPRISE ENTERPRISE 436 ++dep2410 MACH_DEP2410 DEP2410 437 ++armcore MACH_ARMCORE ARMCORE 438 ++hobbit MACH_HOBBIT HOBBIT 439 ++h7210 MACH_H7210 H7210 440 ++pxa_netdcu5 MACH_PXA_NETDCU5 PXA_NETDCU5 441 ++acc MACH_ACC ACC 442 ++esl_sarva MACH_ESL_SARVA ESL_SARVA 443 ++xm250 MACH_XM250 XM250 444 ++t6tc1xb MACH_T6TC1XB T6TC1XB 445 ++ess710 MACH_ESS710 ESS710 446 ++mx3ads MACH_MX3ADS MX3ADS 447 ++himalaya MACH_HIMALAYA HIMALAYA 448 ++bolfenk MACH_BOLFENK BOLFENK 449 ++at91rm9200kr MACH_AT91RM9200KR AT91RM9200KR 450 ++edb9312 MACH_EDB9312 EDB9312 451 ++omap_generic MACH_OMAP_GENERIC OMAP_GENERIC 452 ++aximx3 MACH_AXIMX3 AXIMX3 453 ++eb67xdip MACH_EB67XDIP EB67XDIP 454 ++webtxs MACH_WEBTXS WEBTXS 455 ++hawk MACH_HAWK HAWK 456 ++ccat91sbc001 MACH_CCAT91SBC001 CCAT91SBC001 457 ++expresso MACH_EXPRESSO EXPRESSO 458 ++h4000 MACH_H4000 H4000 459 ++dino MACH_DINO DINO 460 ++ml675k MACH_ML675K ML675K 461 ++edb9301 MACH_EDB9301 EDB9301 462 ++edb9315 MACH_EDB9315 EDB9315 463 ++reciva_tt MACH_RECIVA_TT RECIVA_TT 464 ++cstcb01 MACH_CSTCB01 CSTCB01 465 ++cstcb1 MACH_CSTCB1 CSTCB1 466 ++shadwell MACH_SHADWELL SHADWELL 467 ++goepel263 MACH_GOEPEL263 GOEPEL263 468 ++acq100 MACH_ACQ100 ACQ100 469 ++mx1fs2 MACH_MX1FS2 MX1FS2 470 ++hiptop_g1 MACH_HIPTOP_G1 HIPTOP_G1 471 ++sparky MACH_SPARKY SPARKY 472 ++ns9750 MACH_NS9750 NS9750 473 ++phoenix MACH_PHOENIX PHOENIX 474 ++vr1000 MACH_VR1000 VR1000 475 ++deisterpxa MACH_DEISTERPXA DEISTERPXA 476 ++bcm1160 MACH_BCM1160 BCM1160 477 ++pcm022 MACH_PCM022 PCM022 478 ++adsgcx MACH_ADSGCX ADSGCX 479 ++dreadnaught MACH_DREADNAUGHT DREADNAUGHT 480 ++dm320 MACH_DM320 DM320 481 ++markov MACH_MARKOV MARKOV 482 ++cos7a400 MACH_COS7A400 COS7A400 483 ++milano MACH_MILANO MILANO 484 ++ue9328 MACH_UE9328 UE9328 485 ++uex255 MACH_UEX255 UEX255 486 ++ue2410 MACH_UE2410 UE2410 487 ++a620 MACH_A620 A620 488 ++ocelot MACH_OCELOT OCELOT 489 ++cheetah MACH_CHEETAH CHEETAH 490 ++omap_perseus2 MACH_OMAP_PERSEUS2 OMAP_PERSEUS2 491 ++zvue MACH_ZVUE ZVUE 492 ++roverp1 MACH_ROVERP1 ROVERP1 493 ++asidial2 MACH_ASIDIAL2 ASIDIAL2 494 ++s3c24a0 MACH_S3C24A0 S3C24A0 495 ++e800 MACH_E800 E800 496 ++e750 MACH_E750 E750 497 ++s3c5500 MACH_S3C5500 S3C5500 498 ++smdk5500 MACH_SMDK5500 SMDK5500 499 ++signalsync MACH_SIGNALSYNC SIGNALSYNC 500 ++nbc MACH_NBC NBC 501 ++er4525 MACH_ER4525 ER4525 502 ++netbookpro MACH_NETBOOKPRO NETBOOKPRO 503 ++hw90200 MACH_HW90200 HW90200 504 ++condor MACH_CONDOR CONDOR 505 ++cup MACH_CUP CUP 506 ++kite MACH_KITE KITE 507 ++scb9328 MACH_SCB9328 SCB9328 508 ++omap_h3 MACH_OMAP_H3 OMAP_H3 509 ++omap_h4 MACH_OMAP_H4 OMAP_H4 510 ++n10 MACH_N10 N10 511 ++montajade MACH_MONTAJADE MONTAJADE 512 ++sg560 MACH_SG560 SG560 513 ++dp1000 MACH_DP1000 DP1000 514 ++omap_osk MACH_OMAP_OSK OMAP_OSK 515 ++rg100v3 MACH_RG100V3 RG100V3 516 ++mx2ads MACH_MX2ADS MX2ADS 517 +diff -urN linux-2.4.26/arch/i386/config.in linux-2.4.26-vrs1/arch/i386/config.in +--- linux-2.4.26/arch/i386/config.in 2004-02-27 20:03:23.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/i386/config.in 2004-02-23 13:36:21.000000000 +0000 +@@ -9,6 +9,7 @@ + + define_bool CONFIG_UID16 y + ++define_bool CONFIG_GENERIC_ISA_DMA y + mainmenu_option next_comment + comment 'Code maturity level options' + bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +diff -urN linux-2.4.26/arch/i386/kernel/Makefile linux-2.4.26-vrs1/arch/i386/kernel/Makefile +--- linux-2.4.26/arch/i386/kernel/Makefile 2003-11-28 18:26:19.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/i386/kernel/Makefile 2004-01-14 21:38:44.000000000 +0000 +@@ -7,8 +7,8 @@ + # + # Note 2! The CFLAGS definitions are now in the main makefile... + +-.S.o: +- $(CC) $(AFLAGS) -traditional -c $< -o $*.o ++USE_STANDARD_AS_RULE := true ++EXTRA_AFLAGS := -traditional + + all: kernel.o head.o init_task.o + +diff -urN linux-2.4.26/arch/i386/kernel/apm.c linux-2.4.26-vrs1/arch/i386/kernel/apm.c +--- linux-2.4.26/arch/i386/kernel/apm.c 2003-08-25 12:44:39.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/i386/kernel/apm.c 2004-01-14 21:38:44.000000000 +0000 +@@ -1267,6 +1267,7 @@ + as->suspend_wait = 0; + as->suspend_result = err; + } ++ ignore_normal_resume = 1; + wake_up_interruptible(&apm_suspend_waitqueue); + return err; + } +@@ -1319,6 +1320,8 @@ + if (ignore_bounce + && ((jiffies - last_resume) > bounce_interval)) + ignore_bounce = 0; ++ if (ignore_normal_resume && (event != APM_NORMAL_RESUME)) ++ ignore_normal_resume = 0; + + switch (event) { + case APM_SYS_STANDBY: +diff -urN linux-2.4.26/arch/i386/lib/Makefile linux-2.4.26-vrs1/arch/i386/lib/Makefile +--- linux-2.4.26/arch/i386/lib/Makefile 2001-09-10 15:31:30.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/i386/lib/Makefile 2004-01-14 21:32:24.000000000 +0000 +@@ -2,8 +2,7 @@ + # Makefile for i386-specific library files.. + # + +-.S.o: +- $(CC) $(AFLAGS) -c $< -o $*.o ++USE_STANDARD_AS_RULE := true + + L_TARGET = lib.a + +diff -urN linux-2.4.26/arch/i386/math-emu/Makefile linux-2.4.26-vrs1/arch/i386/math-emu/Makefile +--- linux-2.4.26/arch/i386/math-emu/Makefile 2000-12-29 22:07:20.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/i386/math-emu/Makefile 2004-01-14 21:32:24.000000000 +0000 +@@ -2,15 +2,15 @@ + # Makefile for wm-FPU-emu + # + ++USE_STANDARD_AS_RULE := true ++ + O_TARGET := math.o + + #DEBUG = -DDEBUGGING + DEBUG = + PARANOID = -DPARANOID + CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin $(MATH_EMULATION) +- +-.S.o: +- $(CC) $(AFLAGS) $(PARANOID) -c $< ++EXTRA_AFLAGS := $(PARANOID) + + # From 'C' language sources: + C_OBJS =fpu_entry.o errors.o \ +diff -urN linux-2.4.26/arch/ia64/config.in linux-2.4.26-vrs1/arch/ia64/config.in +--- linux-2.4.26/arch/ia64/config.in 2004-02-27 20:03:23.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/ia64/config.in 2004-02-23 13:36:21.000000000 +0000 +@@ -25,6 +25,7 @@ + define_bool CONFIG_SBUS n + define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n + define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y ++define_bool CONFIG_GENERIC_ISA_DMA y + + choice 'IA-64 processor type' \ + "Itanium CONFIG_ITANIUM \ +diff -urN linux-2.4.26/arch/m68k/config.in linux-2.4.26-vrs1/arch/m68k/config.in +--- linux-2.4.26/arch/m68k/config.in 2004-02-27 20:03:23.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/m68k/config.in 2004-02-23 13:36:22.000000000 +0000 +@@ -6,6 +6,7 @@ + define_bool CONFIG_UID16 y + define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y + define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n ++define_bool CONFIG_GENERIC_ISA_DMA y + + mainmenu_name "Linux/68k Kernel Configuration" + +diff -urN linux-2.4.26/arch/mips/config.in linux-2.4.26-vrs1/arch/mips/config.in +--- linux-2.4.26/arch/mips/config.in 2002-11-28 23:53:09.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/mips/config.in 2004-01-14 21:32:24.000000000 +0000 +@@ -5,5 +5,6 @@ + define_bool CONFIG_MIPS y + define_bool CONFIG_MIPS32 y + define_bool CONFIG_MIPS64 n ++define_bool CONFIG_GENERIC_ISA_DMA y + + source arch/mips/config-shared.in +diff -urN linux-2.4.26/arch/parisc/config.in linux-2.4.26-vrs1/arch/parisc/config.in +--- linux-2.4.26/arch/parisc/config.in 2004-02-27 20:03:24.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/parisc/config.in 2004-02-23 13:36:26.000000000 +0000 +@@ -9,6 +9,7 @@ + define_bool CONFIG_UID16 n + define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y + define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n ++define_bool CONFIG_GENERIC_ISA_DMA y + + mainmenu_option next_comment + comment 'Code maturity level options' +diff -urN linux-2.4.26/arch/ppc/config.in linux-2.4.26-vrs1/arch/ppc/config.in +--- linux-2.4.26/arch/ppc/config.in 2004-04-19 11:44:15.000000000 +0100 ++++ linux-2.4.26-vrs1/arch/ppc/config.in 2004-04-18 21:47:50.000000000 +0100 +@@ -6,6 +6,7 @@ + define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n + define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y + define_bool CONFIG_HAVE_DEC_LOCK y ++define_bool CONFIG_GENERIC_ISA_DMA y + + mainmenu_name "Linux/PowerPC Kernel Configuration" + +diff -urN linux-2.4.26/arch/sh/config.in linux-2.4.26-vrs1/arch/sh/config.in +--- linux-2.4.26/arch/sh/config.in 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/sh/config.in 2004-02-23 13:36:27.000000000 +0000 +@@ -9,6 +9,7 @@ + define_bool CONFIG_UID16 y + define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y + define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n ++define_bool CONFIG_GENERIC_ISA_DMA y + + mainmenu_option next_comment + comment 'Code maturity level options' +diff -urN linux-2.4.26/arch/sparc/config.in linux-2.4.26-vrs1/arch/sparc/config.in +--- linux-2.4.26/arch/sparc/config.in 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/sparc/config.in 2004-02-23 13:36:27.000000000 +0000 +@@ -6,6 +6,7 @@ + + define_bool CONFIG_UID16 y + define_bool CONFIG_HIGHMEM y ++define_bool CONFIG_GENERIC_ISA_DMA y + + mainmenu_option next_comment + comment 'Code maturity level options' +diff -urN linux-2.4.26/arch/sparc64/config.in linux-2.4.26-vrs1/arch/sparc64/config.in +--- linux-2.4.26/arch/sparc64/config.in 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/arch/sparc64/config.in 2004-02-23 13:36:27.000000000 +0000 +@@ -41,6 +41,7 @@ + define_bool CONFIG_HAVE_DEC_LOCK y + define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n + define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y ++define_bool CONFIG_GENERIC_ISA_DMA y + define_bool CONFIG_ISA n + define_bool CONFIG_ISAPNP n + define_bool CONFIG_EISA n +diff -urN linux-2.4.26/drivers/Makefile linux-2.4.26-vrs1/drivers/Makefile +--- linux-2.4.26/drivers/Makefile 2003-11-28 18:26:19.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/Makefile 2004-02-04 04:11:33.000000000 +0000 +@@ -8,9 +8,9 @@ + + mod-subdirs := dio hil mtd sbus video macintosh usb input telephony ide \ + message/i2o message/fusion scsi md ieee1394 pnp isdn atm \ +- fc4 net/hamradio i2c acpi bluetooth usb/gadget ++ fc4 net/hamradio i2c l3 acpi bluetooth serial usb/gadget + +-subdir-y := parport char block net sound misc media cdrom hotplug ++subdir-y := parport serial char block net sound misc media cdrom hotplug pld + subdir-m := $(subdir-y) + + +@@ -45,8 +45,12 @@ + # CONFIG_HAMRADIO can be set without CONFIG_NETDEVICE being set -- ch + subdir-$(CONFIG_HAMRADIO) += net/hamradio + subdir-$(CONFIG_I2C) += i2c ++subdir-$(CONFIG_L3) += l3 + subdir-$(CONFIG_ACPI_BOOT) += acpi + + subdir-$(CONFIG_BLUEZ) += bluetooth ++subdir-$(CONFIG_SSI) += ssi ++ ++subdir-$(CONFIG_ARCH_AT91RM9200)+= at91 + + include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/acorn/char/i2c.c linux-2.4.26-vrs1/drivers/acorn/char/i2c.c +--- linux-2.4.26/drivers/acorn/char/i2c.c 2004-01-05 13:53:56.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/acorn/char/i2c.c 2004-04-10 11:43:00.000000000 +0100 +@@ -33,9 +33,13 @@ + static struct i2c_client *rtc_client; + static const unsigned char days_in_mon[] = + { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +-static unsigned int rtc_epoch = 1900; + + #define CMOS_CHECKSUM (63) ++ ++/* ++ * Acorn machines store the year in the static RAM at ++ * location 128. ++ */ + #define CMOS_YEAR (64 + 128) + + static inline int rtc_command(int cmd, void *data) +@@ -49,51 +53,91 @@ + } + + /* ++ * Update the century + year bytes in the CMOS RAM, ensuring ++ * that the check byte is correctly adjusted for the change. ++ */ ++static int rtc_update_year(unsigned int new_year) ++{ ++ unsigned char yr[2], chk; ++ struct mem cmos_year = { CMOS_YEAR, sizeof(yr), yr }; ++ struct mem cmos_check = { CMOS_CHECKSUM, 1, &chk }; ++ int ret; ++ ++ ret = rtc_command(MEM_READ, &cmos_check); ++ if (ret) ++ goto out; ++ ret = rtc_command(MEM_READ, &cmos_year); ++ if (ret) ++ goto out; ++ ++ chk -= yr[1] + yr[0]; ++ ++ yr[1] = new_year / 100; ++ yr[0] = new_year % 100; ++ ++ chk += yr[1] + yr[0]; ++ ++ ret = rtc_command(MEM_WRITE, &cmos_year); ++ if (ret == 0) ++ ret = rtc_command(MEM_WRITE, &cmos_check); ++ out: ++ return ret; ++} ++ ++ ++/* + * Read the current RTC time and date, and update xtime. + */ + static void get_rtc_time(struct rtc_tm *rtctm, unsigned int *year) + { + unsigned char ctrl, yr[2]; + struct mem rtcmem = { CMOS_YEAR, sizeof(yr), yr }; ++ int real_year, year_offset; + + /* + * Ensure that the RTC is running. + */ + rtc_command(RTC_GETCTRL, &ctrl); + if (ctrl & 0xc0) { +- unsigned char new_ctrl; +- +- new_ctrl = ctrl & ~0xc0; ++ unsigned char new_ctrl = ctrl & ~0xc0; + +- printk("RTC: resetting control %02X -> %02X\n", +- ctrl, new_ctrl); ++ printk(KERN_WARNING "RTC: resetting control %02x -> %02x\n", ++ ctrl, new_ctrl); + + rtc_command(RTC_SETCTRL, &new_ctrl); + } + ++ if (rtc_command(RTC_GETDATETIME, rtctm) || ++ rtc_command(MEM_READ, &rtcmem)) ++ return; ++ ++ real_year = yr[0]; ++ + /* +- * Acorn machines store the year in +- * the static RAM at location 192. ++ * The RTC year holds the LSB two bits of the current ++ * year, which should reflect the LSB two bits of the ++ * CMOS copy of the year. Any difference indicates ++ * that we have to correct the CMOS version. + */ +- if (rtc_command(MEM_READ, &rtcmem)) +- return; ++ year_offset = rtctm->year_off - (real_year & 3); ++ if (year_offset < 0) ++ /* ++ * RTC year wrapped. Adjust it appropriately. ++ */ ++ year_offset += 4; + +- if (rtc_command(RTC_GETDATETIME, rtctm)) +- return; ++ *year = real_year + year_offset + yr[1] * 100; + +- *year = yr[1] * 100 + yr[0]; + } + + static int set_rtc_time(struct rtc_tm *rtctm, unsigned int year) + { +- unsigned char yr[2], leap, chk; +- struct mem cmos_year = { CMOS_YEAR, sizeof(yr), yr }; +- struct mem cmos_check = { CMOS_CHECKSUM, 1, &chk }; ++ unsigned char leap; + int ret; + + leap = (!(year % 4) && (year % 100)) || !(year % 400); + +- if (rtctm->mon > 12 || rtctm->mday == 0) ++ if (rtctm->mon > 12 || rtctm->mon == 0 || rtctm->mday == 0) + return -EINVAL; + + if (rtctm->mday > (days_in_mon[rtctm->mon] + (rtctm->mon == 2 && leap))) +@@ -102,21 +146,16 @@ + if (rtctm->hours >= 24 || rtctm->mins >= 60 || rtctm->secs >= 60) + return -EINVAL; + +- ret = rtc_command(RTC_SETDATETIME, rtctm); +- if (ret == 0) { +- rtc_command(MEM_READ, &cmos_check); +- rtc_command(MEM_READ, &cmos_year); +- +- chk -= yr[1] + yr[0]; +- +- yr[1] = year / 100; +- yr[0] = year % 100; ++ /* ++ * The RTC's own 2-bit year must reflect the least ++ * significant two bits of the CMOS year. ++ */ ++ rtctm->year_off = (year % 100) & 3; + +- chk += yr[1] + yr[0]; ++ ret = rtc_command(RTC_SETDATETIME, rtctm); ++ if (ret == 0) ++ ret = rtc_update_year(year); + +- rtc_command(MEM_WRITE, &cmos_year); +- rtc_command(MEM_WRITE, &cmos_check); +- } + return ret; + } + +@@ -166,7 +205,6 @@ + break; + + case RTC_RD_TIME: +- memset(&rtctm, 0, sizeof(struct rtc_time)); + get_rtc_time(&rtc_raw, &year); + rtctm.tm_sec = rtc_raw.secs; + rtctm.tm_min = rtc_raw.mins; +@@ -188,13 +226,12 @@ + rtc_raw.hours = rtctm.tm_hour; + rtc_raw.mday = rtctm.tm_mday; + rtc_raw.mon = rtctm.tm_mon + 1; +- rtc_raw.year_off = 2; + year = rtctm.tm_year + 1900; + return set_rtc_time(&rtc_raw, year); + break; + + case RTC_EPOCH_READ: +- return put_user(rtc_epoch, (unsigned long *)arg); ++ return put_user(1900, (unsigned long *)arg); + + } + return -EINVAL; +diff -urN linux-2.4.26/drivers/acorn/net/ether1.c linux-2.4.26-vrs1/drivers/acorn/net/ether1.c +--- linux-2.4.26/drivers/acorn/net/ether1.c 2003-08-25 12:44:40.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/acorn/net/ether1.c 2004-04-09 19:55:21.000000000 +0100 +@@ -80,7 +80,7 @@ + #define BUS_16 16 + #define BUS_8 8 + +-static const card_ids __init ether1_cids[] = { ++static card_ids __initdata ether1_cids[] = { + { MANU_ACORN, PROD_ACORN_ETHER1 }, + { 0xffff, 0xffff } + }; +@@ -153,35 +153,35 @@ + length -= thislen; + + __asm__ __volatile__( +- "subs %3, %3, #2 +- bmi 2f +-1: ldr %0, [%1], #2 +- mov %0, %0, lsl #16 +- orr %0, %0, %0, lsr #16 +- str %0, [%2], #4 +- subs %3, %3, #2 +- bmi 2f +- ldr %0, [%1], #2 +- mov %0, %0, lsl #16 +- orr %0, %0, %0, lsr #16 +- str %0, [%2], #4 +- subs %3, %3, #2 +- bmi 2f +- ldr %0, [%1], #2 +- mov %0, %0, lsl #16 +- orr %0, %0, %0, lsr #16 +- str %0, [%2], #4 +- subs %3, %3, #2 +- bmi 2f +- ldr %0, [%1], #2 +- mov %0, %0, lsl #16 +- orr %0, %0, %0, lsr #16 +- str %0, [%2], #4 +- subs %3, %3, #2 +- bpl 1b +-2: adds %3, %3, #1 +- ldreqb %0, [%1] +- streqb %0, [%2]" ++" subs %3, %3, #2 \n" ++" bmi 2f \n" ++"1: ldr %0, [%1], #2 \n" ++" mov %0, %0, lsl #16 \n" ++" orr %0, %0, %0, lsr #16 \n" ++" str %0, [%2], #4 \n" ++" subs %3, %3, #2 \n" ++" bmi 2f \n" ++" ldr %0, [%1], #2 \n" ++" mov %0, %0, lsl #16 \n" ++" orr %0, %0, %0, lsr #16 \n" ++" str %0, [%2], #4 \n" ++" subs %3, %3, #2 \n" ++" bmi 2f \n" ++" ldr %0, [%1], #2 \n" ++" mov %0, %0, lsl #16 \n" ++" orr %0, %0, %0, lsr #16 \n" ++" str %0, [%2], #4 \n" ++" subs %3, %3, #2 \n" ++" bmi 2f \n" ++" ldr %0, [%1], #2 \n" ++" mov %0, %0, lsl #16 \n" ++" orr %0, %0, %0, lsr #16 \n" ++" str %0, [%2], #4 \n" ++" subs %3, %3, #2 \n" ++" bpl 1b \n" ++"2: adds %3, %3, #1 \n" ++" ldreqb %0, [%1] \n" ++" streqb %0, [%2] \n" + : "=&r" (used), "=&r" (data) + : "r" (addr), "r" (thislen), "1" (data)); + +@@ -215,35 +215,35 @@ + length -= thislen; + + __asm__ __volatile__( +- "subs %3, %3, #2 +- bmi 2f +-1: ldr %0, [%2], #4 +- strb %0, [%1], #1 +- mov %0, %0, lsr #8 +- strb %0, [%1], #1 +- subs %3, %3, #2 +- bmi 2f +- ldr %0, [%2], #4 +- strb %0, [%1], #1 +- mov %0, %0, lsr #8 +- strb %0, [%1], #1 +- subs %3, %3, #2 +- bmi 2f +- ldr %0, [%2], #4 +- strb %0, [%1], #1 +- mov %0, %0, lsr #8 +- strb %0, [%1], #1 +- subs %3, %3, #2 +- bmi 2f +- ldr %0, [%2], #4 +- strb %0, [%1], #1 +- mov %0, %0, lsr #8 +- strb %0, [%1], #1 +- subs %3, %3, #2 +- bpl 1b +-2: adds %3, %3, #1 +- ldreqb %0, [%2] +- streqb %0, [%1]" ++" subs %3, %3, #2 \n" ++" bmi 2f \n" ++"1: ldr %0, [%2], #4 \n" ++" strb %0, [%1], #1 \n" ++" mov %0, %0, lsr #8 \n" ++" strb %0, [%1], #1 \n" ++" subs %3, %3, #2 \n" ++" bmi 2f \n" ++" ldr %0, [%2], #4 \n" ++" strb %0, [%1], #1 \n" ++" mov %0, %0, lsr #8 \n" ++" strb %0, [%1], #1 \n" ++" subs %3, %3, #2 \n" ++" bmi 2f \n" ++" ldr %0, [%2], #4 \n" ++" strb %0, [%1], #1 \n" ++" mov %0, %0, lsr #8 \n" ++" strb %0, [%1], #1 \n" ++" subs %3, %3, #2 \n" ++" bmi 2f \n" ++" ldr %0, [%2], #4 \n" ++" strb %0, [%1], #1 \n" ++" mov %0, %0, lsr #8 \n" ++" strb %0, [%1], #1 \n" ++" subs %3, %3, #2 \n" ++" bpl 1b \n" ++"2: adds %3, %3, #1 \n" ++" ldreqb %0, [%2] \n" ++" streqb %0, [%1] \n" + : "=&r" (used), "=&r" (data) + : "r" (addr), "r" (thislen), "1" (data)); + +diff -urN linux-2.4.26/drivers/acorn/net/ether3.c linux-2.4.26-vrs1/drivers/acorn/net/ether3.c +--- linux-2.4.26/drivers/acorn/net/ether3.c 2003-08-25 12:44:40.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/acorn/net/ether3.c 2004-01-14 21:32:24.000000000 +0000 +@@ -75,7 +75,7 @@ + #include "ether3.h" + + static unsigned int net_debug = NET_DEBUG; +-static const card_ids __init ether3_cids[] = { ++static card_ids __initdata ether3_cids[] = { + { MANU_ANT2, PROD_ANT_ETHER3 }, + { MANU_ANT, PROD_ANT_ETHER3 }, + { MANU_ANT, PROD_ANT_ETHERB }, +diff -urN linux-2.4.26/drivers/acorn/net/etherh.c linux-2.4.26-vrs1/drivers/acorn/net/etherh.c +--- linux-2.4.26/drivers/acorn/net/etherh.c 2003-08-25 12:44:40.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/acorn/net/etherh.c 2004-01-14 21:32:24.000000000 +0000 +@@ -57,7 +57,7 @@ + + static unsigned int net_debug = NET_DEBUG; + +-static const card_ids __init etherh_cids[] = { ++static card_ids __initdata etherh_cids[] = { + { MANU_ANT, PROD_ANT_ETHERM }, + { MANU_I3, PROD_I3_ETHERLAN500 }, + { MANU_I3, PROD_I3_ETHERLAN600 }, +diff -urN linux-2.4.26/drivers/acorn/scsi/cumana_1.c linux-2.4.26-vrs1/drivers/acorn/scsi/cumana_1.c +--- linux-2.4.26/drivers/acorn/scsi/cumana_1.c 2001-09-13 23:21:32.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/acorn/scsi/cumana_1.c 2004-03-07 11:05:38.000000000 +0000 +@@ -153,20 +153,20 @@ + ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0; + outb(0x00, instance->io_port - 577); + +- if (instance->irq != IRQ_NONE) ++ if (instance->irq != SCSI_IRQ_NONE) + if (request_irq(instance->irq, do_cumanascsi_intr, SA_INTERRUPT, "CumanaSCSI-1", NULL)) { + printk("scsi%d: IRQ%d not free, interrupts disabled\n", + instance->host_no, instance->irq); +- instance->irq = IRQ_NONE; ++ instance->irq = SCSI_IRQ_NONE; + } + +- if (instance->irq == IRQ_NONE) { ++ if (instance->irq == SCSI_IRQ_NONE) { + printk("scsi%d: interrupts not enabled. for better interactive performance,\n", instance->host_no); + printk("scsi%d: please jumper the board for a free IRQ.\n", instance->host_no); + } + + printk("scsi%d: at port %lX irq", instance->host_no, instance->io_port); +- if (instance->irq == IRQ_NONE) ++ if (instance->irq == SCSI_IRQ_NONE) + printk ("s disabled"); + else + printk (" %d", instance->irq); +@@ -185,7 +185,7 @@ + { + int i; + +- if (shpnt->irq != IRQ_NONE) ++ if (shpnt->irq != SCSI_IRQ_NONE) + free_irq (shpnt->irq, NULL); + if (shpnt->io_port) + release_region (shpnt->io_port, shpnt->n_io_port); +diff -urN linux-2.4.26/drivers/acorn/scsi/ecoscsi.c linux-2.4.26-vrs1/drivers/acorn/scsi/ecoscsi.c +--- linux-2.4.26/drivers/acorn/scsi/ecoscsi.c 2002-08-03 01:39:43.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/acorn/scsi/ecoscsi.c 2004-03-07 11:06:45.000000000 +0000 +@@ -106,7 +106,7 @@ + instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); + instance->io_port = 0x80ce8000; + instance->n_io_port = 144; +- instance->irq = IRQ_NONE; ++ instance->irq = SCSI_IRQ_NONE; + + if (check_region (instance->io_port, instance->n_io_port)) { + scsi_unregister (instance); +@@ -130,20 +130,20 @@ + return 0; + } + +- if (instance->irq != IRQ_NONE) ++ if (instance->irq != SCSI_IRQ_NONE) + if (request_irq(instance->irq, do_ecoscsi_intr, SA_INTERRUPT, "ecoscsi", NULL)) { + printk("scsi%d: IRQ%d not free, interrupts disabled\n", + instance->host_no, instance->irq); +- instance->irq = IRQ_NONE; ++ instance->irq = SCSI_IRQ_NONE; + } + +- if (instance->irq != IRQ_NONE) { ++ if (instance->irq != SCSI_IRQ_NONE) { + printk("scsi%d: eek! Interrupts enabled, but I don't think\n", instance->host_no); + printk("scsi%d: that the board had an interrupt!\n", instance->host_no); + } + + printk("scsi%d: at port %X irq", instance->host_no, instance->io_port); +- if (instance->irq == IRQ_NONE) ++ if (instance->irq == SCSI_IRQ_NONE) + printk ("s disabled"); + else + printk (" %d", instance->irq); +@@ -157,7 +157,7 @@ + + int ecoscsi_release (struct Scsi_Host *shpnt) + { +- if (shpnt->irq != IRQ_NONE) ++ if (shpnt->irq != SCSI_IRQ_NONE) + free_irq (shpnt->irq, NULL); + if (shpnt->io_port) + release_region (shpnt->io_port, shpnt->n_io_port); +diff -urN linux-2.4.26/drivers/acorn/scsi/oak.c linux-2.4.26-vrs1/drivers/acorn/scsi/oak.c +--- linux-2.4.26/drivers/acorn/scsi/oak.c 2001-10-11 17:04:57.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/acorn/scsi/oak.c 2004-03-07 11:05:55.000000000 +0000 +@@ -97,7 +97,7 @@ + }; + + #define OAK_ADDRESS(card) (ecard_address((card), ECARD_MEMC, 0)) +-#define OAK_IRQ(card) (IRQ_NONE) ++#define OAK_IRQ(card) (SCSI_IRQ_NONE) + /* + * Function : int oakscsi_detect(Scsi_Host_Template * tpnt) + * +@@ -136,20 +136,20 @@ + instance->n_io_port = 255; + request_region (instance->io_port, instance->n_io_port, "Oak SCSI"); + +- if (instance->irq != IRQ_NONE) ++ if (instance->irq != SCSI_IRQ_NONE) + if (request_irq(instance->irq, do_oakscsi_intr, SA_INTERRUPT, "Oak SCSI", NULL)) { + printk("scsi%d: IRQ%d not free, interrupts disabled\n", + instance->host_no, instance->irq); +- instance->irq = IRQ_NONE; ++ instance->irq = SCSI_IRQ_NONE; + } + +- if (instance->irq != IRQ_NONE) { ++ if (instance->irq != SCSI_IRQ_NONE) { + printk("scsi%d: eek! Interrupts enabled, but I don't think\n", instance->host_no); + printk("scsi%d: that the board had an interrupt!\n", instance->host_no); + } + + printk("scsi%d: at port %lX irq", instance->host_no, instance->io_port); +- if (instance->irq == IRQ_NONE) ++ if (instance->irq == SCSI_IRQ_NONE) + printk ("s disabled"); + else + printk (" %d", instance->irq); +@@ -172,7 +172,7 @@ + { + int i; + +- if (shpnt->irq != IRQ_NONE) ++ if (shpnt->irq != SCSI_IRQ_NONE) + free_irq (shpnt->irq, NULL); + if (shpnt->io_port) + release_region (shpnt->io_port, shpnt->n_io_port); +diff -urN linux-2.4.26/drivers/at91/Makefile linux-2.4.26-vrs1/drivers/at91/Makefile +--- linux-2.4.26/drivers/at91/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,23 @@ ++# ++# Makefile for the AT91RM9200-specific Linux kernel device drivers. ++# ++# Note! Dependencies are done automagically by 'make dep', which also ++# removes any old dependencies. DON'T put your own dependencies here ++# unless it's something special (not a .c file). ++ ++O_TARGET := at91drv.o ++ ++subdir-y := serial net watchdog rtc usb i2c spi mtd ++subdir-m := $(subdir-y) ++ ++obj-$(CONFIG_SERIAL_AT91) += serial/at91serial.o ++obj-$(CONFIG_AT91_ETHER) += net/at91net.o ++obj-$(CONFIG_AT91_WATCHDOG) += watchdog/at91wdt.o ++obj-$(CONFIG_AT91_RTC) += rtc/at91rtc.o ++obj-$(CONFIG_USB) += usb/at91usb.o ++obj-$(CONFIG_I2C_AT91) += i2c/at91i2c.o ++obj-$(CONFIG_AT91_SPIDEV) += spi/at91spi.o ++obj-$(CONFIG_MTD_AT91_DATAFLASH) += spi/at91spi.o mtd/at91mtd.o ++obj-$(CONFIG_MTD_AT91_SMARTMEDIA) += mtd/at91mtd.o ++ ++include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/at91/i2c/Makefile linux-2.4.26-vrs1/drivers/at91/i2c/Makefile +--- linux-2.4.26/drivers/at91/i2c/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/i2c/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,15 @@ ++# File: drivers/at91/i2c/Makefile ++# ++# Makefile for the Atmel AT91RM9200 I2C (TWI) device drivers ++# ++ ++O_TARGET := at91i2c.o ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++obj-$(CONFIG_I2C_AT91) += at91_i2c.o ++ ++include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/at91/i2c/at91_i2c.c linux-2.4.26-vrs1/drivers/at91/i2c/at91_i2c.c +--- linux-2.4.26/drivers/at91/i2c/at91_i2c.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/i2c/at91_i2c.c 2004-03-07 15:59:15.000000000 +0000 +@@ -0,0 +1,257 @@ ++/* ++ i2c Support for Atmel's AT91RM9200 Two-Wire Interface ++ ++ (c) Rick Bronson ++ ++ Borrowed heavily from original work by: ++ Copyright (c) 2000 Philip Edelbrock ++ ++ 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "at91_i2c.h" ++ ++#define DBG(x...) do {\ ++ if (debug > 0) \ ++ printk(KERN_DEBUG "i2c:" x); \ ++ } while(0) ++ ++int debug = 0; ++ ++static struct at91_i2c_local *at91_i2c_device; ++ ++/* ++ * Poll the i2c status register until the specified bit is set. ++ * Returns 0 if timed out (100 msec) ++ */ ++static short at91_poll_status(AT91PS_TWI twi, unsigned long bit) { ++ int loop_cntr = 10000; ++ do { ++ udelay(10); ++ } while (!(twi->TWI_SR & bit) && (--loop_cntr > 0)); ++ ++ return (loop_cntr > 0); ++} ++ ++/* ++ * Generic i2c master transfer entrypoint ++ */ ++static int at91_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) ++{ ++ struct at91_i2c_local *device = (struct at91_i2c_local *) adap->data; ++ AT91PS_TWI twi = (AT91PS_TWI) device->base_addr; ++ ++ struct i2c_msg *pmsg; ++ int length; ++ unsigned char *buf; ++ ++ /* ++ * i2c_smbus_xfer_emulated() in drivers/i2c/i2c-core.c states: ++ * "... In the case of writing, we need to use only one message; ++ * when reading, we need two..." ++ */ ++ ++ pmsg = msgs; /* look at 1st message, it contains the address/command */ ++ if (num >= 1 && num <= 2) { ++ DBG("xfer: doing %s %d bytes to 0x%02x - %d messages\n", ++ pmsg->flags & I2C_M_RD ? "read" : "write", ++ pmsg->len, pmsg->buf[0], num); ++ ++ /* Set the TWI Master Mode Register */ ++ twi->TWI_MMR = (pmsg->addr << 16) | (pmsg->len << 8) ++ | ((pmsg + 1)->flags & I2C_M_RD ? AT91C_TWI_MREAD : 0); ++ ++ /* Set TWI Internal Address Register with first messages data field */ ++ if (pmsg->len == 1) ++ twi->TWI_IADR = pmsg->buf[0]; ++ else if (pmsg->len == 2) ++ twi->TWI_IADR = pmsg->buf[0] << 8 | pmsg->buf[1]; ++ else /* must be 3 */ ++ twi->TWI_IADR = pmsg->buf[0] << 16 | pmsg->buf[1] << 8 | pmsg->buf[2]; ++ ++ /* 1st message contains the address/command */ ++ if (num > 1) ++ pmsg++; /* go to real message */ ++ ++ length = pmsg->len; ++ buf = pmsg->buf; ++ if (length && buf) { /* sanity check */ ++ if (pmsg->flags & I2C_M_RD) { ++ twi->TWI_CR = AT91C_TWI_START; ++ while (length--) { ++ if (!length) ++ twi->TWI_CR = AT91C_TWI_STOP; ++ /* Wait until transfer is finished */ ++ if (!at91_poll_status(twi, AT91C_TWI_RXRDY)) { ++ printk(KERN_ERR "at91_i2c: timeout 1\n"); ++ return 0; ++ } ++ *buf++ = twi->TWI_RHR; ++ } ++ if (!at91_poll_status(twi, AT91C_TWI_TXCOMP)) { ++ printk(KERN_ERR "at91_i2c: timeout 2\n"); ++ return 0; ++ } ++ } else { ++ twi->TWI_CR = AT91C_TWI_START; ++ while (length--) { ++ twi->TWI_THR = *buf++; ++ if (!length) ++ twi->TWI_CR = AT91C_TWI_STOP; ++ if (!at91_poll_status(twi, AT91C_TWI_TXRDY)) { ++ printk(KERN_ERR "at91_i2c: timeout 3\n"); ++ return 0; ++ } ++ } ++ /* Wait until transfer is finished */ ++ if (!at91_poll_status(twi, AT91C_TWI_TXCOMP)) { ++ printk(KERN_ERR "at91_i2c: timeout 4\n"); ++ return 0; ++ } ++ } ++ } ++ DBG("transfer complete\n"); ++ return num; ++ } ++ else { ++ printk(KERN_ERR "at91_i2c: unexpected number of messages: %d\n", num); ++ return 0; ++ } ++} ++ ++/* ++ * Return list of supported functionality ++ */ ++static u32 at91_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE ++ | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA ++ | I2C_FUNC_SMBUS_BLOCK_DATA; ++} ++ ++/* ++ * Open ++ */ ++static void at91_inc(struct i2c_adapter *adapter) ++{ ++ MOD_INC_USE_COUNT; ++} ++ ++/* ++ * Close ++ */ ++static void at91_dec(struct i2c_adapter *adapter) ++{ ++ MOD_DEC_USE_COUNT; ++} ++ ++/* For now, we only handle combined mode (smbus) */ ++static struct i2c_algorithm at91_algorithm = { ++ name:"at91 i2c", ++ id:I2C_ALGO_SMBUS, ++ master_xfer:at91_xfer, ++ functionality:at91_func, ++}; ++ ++/* ++ * Main initialization routine ++ */ ++static int __init i2c_at91_init(void) ++{ ++ AT91PS_TWI twi = (AT91PS_TWI) AT91C_VA_BASE_TWI; ++ struct at91_i2c_local *device; ++ int rc; ++ ++ AT91_CfgPIO_TWI(); ++ AT91_SYS->PMC_PCER = 1 << AT91C_ID_TWI; /* enable peripheral clock */ ++ ++ twi->TWI_IDR = 0x3ff; /* Disable all interrupts */ ++ twi->TWI_CR = AT91C_TWI_SWRST; /* Reset peripheral */ ++ twi->TWI_CR = AT91C_TWI_MSEN | AT91C_TWI_SVDIS; /* Set Master mode */ ++ ++ /* Here, CKDIV = 1 and CHDIV=CLDIV ==> CLDIV = CHDIV = 1/4*((Fmclk/FTWI) -6) */ ++ twi->TWI_CWGR = AT91C_TWI_CKDIV1 | AT91C_TWI_CLDIV3 | (AT91C_TWI_CLDIV3 << 8); ++ ++ device = (struct at91_i2c_local *) kmalloc(sizeof(struct at91_i2c_local), GFP_KERNEL); ++ if (device == NULL) { ++ printk(KERN_ERR "at91_i2c: can't allocate inteface!\n"); ++ return -ENOMEM; ++ } ++ memset(device, 0, sizeof(struct at91_i2c_local)); ++ at91_i2c_device = device; ++ ++ sprintf(device->adapter.name, "AT91RM9200"); ++ device->adapter.data = (void *) device; ++ device->adapter.id = I2C_ALGO_SMBUS; ++ device->adapter.algo = &at91_algorithm; ++ device->adapter.algo_data = NULL; ++ device->adapter.inc_use = at91_inc; ++ device->adapter.dec_use = at91_dec; ++ device->adapter.client_register = NULL; ++ device->adapter.client_unregister = NULL; ++ device->base_addr = AT91C_VA_BASE_TWI; ++ ++ rc = i2c_add_adapter(&device->adapter); ++ if (rc) { ++ printk(KERN_ERR "at91_i2c: Adapter %s registration failed\n", device->adapter.name); ++ device->adapter.data = NULL; ++ kfree(device); ++ } ++ else ++ printk(KERN_INFO "Found AT91 i2c\n"); ++ return rc; ++} ++ ++/* ++ * Clean up routine ++ */ ++static void __exit i2c_at91_cleanup(void) ++{ ++ struct at91_i2c_local *device = at91_i2c_device; ++ int rc; ++ ++ rc = i2c_del_adapter(&device->adapter); ++ device->adapter.data = NULL; ++ kfree(device); ++ ++ AT91_SYS->PMC_PCDR = 1 << AT91C_ID_TWI; /* disable peripheral clock */ ++ ++ /* We aren't that prepared to deal with this... */ ++ if (rc) ++ printk(KERN_ERR "at91_i2c: i2c_del_adapter failed (%i), that's bad!\n", rc); ++} ++ ++module_init(i2c_at91_init); ++module_exit(i2c_at91_cleanup); ++ ++MODULE_AUTHOR("Rick Bronson"); ++MODULE_DESCRIPTION("I2C driver for Atmel AT91RM9200"); ++MODULE_LICENSE("GPL"); ++MODULE_PARM(debug, "i"); ++ ++EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/at91/i2c/at91_i2c.h linux-2.4.26-vrs1/drivers/at91/i2c/at91_i2c.h +--- linux-2.4.26/drivers/at91/i2c/at91_i2c.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/i2c/at91_i2c.h 2004-03-07 15:59:15.000000000 +0000 +@@ -0,0 +1,43 @@ ++/* ++ i2c Support for Atmel's AT91RM9200 Two-Wire Interface ++ ++ (c) Rick Bronson ++ ++ 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef AT91_I2C_H ++#define AT91_I2C_H ++ ++#define AT91C_TWI_CLOCK 100000 ++#define AT91C_TWI_SCLOCK (10 * AT91C_MASTER_CLOCK / AT91C_TWI_CLOCK) ++#define AT91C_TWI_CKDIV1 (2 << 16) /* TWI clock divider. NOTE: see Errata #22 */ ++ ++#if (AT91C_TWI_SCLOCK % 10) >= 5 ++#define AT91C_TWI_CLDIV2 ((AT91C_TWI_SCLOCK / 10) - 5) ++#else ++#define AT91C_TWI_CLDIV2 ((AT91C_TWI_SCLOCK / 10) - 6) ++#endif ++#define AT91C_TWI_CLDIV3 ((AT91C_TWI_CLDIV2 + (4 - AT91C_TWI_CLDIV2 % 4)) >> 2) ++ ++#define AT91C_EEPROM_I2C_ADDRESS (0x50 << 16) ++ ++/* Physical interface */ ++struct at91_i2c_local { ++ struct i2c_adapter adapter; ++ unsigned long base_addr; ++}; ++ ++#endif +diff -urN linux-2.4.26/drivers/at91/mtd/Makefile linux-2.4.26-vrs1/drivers/at91/mtd/Makefile +--- linux-2.4.26/drivers/at91/mtd/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/mtd/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,19 @@ ++# File: drivers/at91/mtd/Makefile ++# ++# Makefile for the Atmel AT91RM9200 MTD devices. ++# Includes: NAND flash (SmartMedia) & DataFlash ++# ++ ++O_TARGET := at91mtd.o ++ ++export-objs := ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++obj-$(CONFIG_MTD_AT91_DATAFLASH) += at91_dataflash.o ++obj-$(CONFIG_MTD_AT91_SMARTMEDIA) += at91_nand.o ++ ++include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/at91/mtd/at91_dataflash.c linux-2.4.26-vrs1/drivers/at91/mtd/at91_dataflash.c +--- linux-2.4.26/drivers/at91/mtd/at91_dataflash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/mtd/at91_dataflash.c 2004-04-10 12:48:53.000000000 +0100 +@@ -0,0 +1,509 @@ ++/* ++ * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder) ++ * ++ * (c) SAN People (Pty) Ltd ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "at91_dataflash.h" ++#include "../spi/at91_spi.h" ++ ++#undef DEBUG_DATAFLASH ++ ++/* Detected DataFlash devices */ ++static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES]; ++static int nr_devices = 0; ++ ++/* ......................................................................... */ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ ++static struct mtd_partition *mtd_parts = 0; ++static int mtd_parts_nr = 0; ++ ++#define NB_OF(x) (sizeof(x)/sizeof(x[0])) ++ ++static struct mtd_partition static_partitions[] = ++{ ++ { ++ name: "bootloader", ++ offset: 0, ++ size: 64 * 1024, /* 64 Kb */ ++ mask_flags: MTD_WRITEABLE /* read-only */ ++ }, ++ { ++ name: "kernel", ++ offset: MTDPART_OFS_NXTBLK, ++ size: 768 *1024, /* 768 Kb */ ++ }, ++ { ++ name: "filesystem", ++ offset: MTDPART_OFS_NXTBLK, ++ size: MTDPART_SIZ_FULL, ++ } ++}; ++ ++int parse_cmdline_partitions(struct mtd_info *master, ++ struct mtd_partition **pparts, const char *mtd_id); ++ ++#endif ++ ++/* ......................................................................... */ ++ ++/* Allocate a single SPI transfer descriptor. We're assuming that if multiple ++ SPI transfers occur at the same time, spi_access_bus() will serialize them. ++ If this is not valid, then either (i) each dataflash 'priv' structure ++ needs it's own transfer descriptor, (ii) we lock this one, or (iii) use ++ another mechanism. */ ++struct spi_transfer_list* spi_transfer_desc; ++ ++/* ++ * Perform a SPI transfer to access the DataFlash device. ++ */ ++int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len, ++ char* txnext, int txnext_len, char* rxnext, int rxnext_len) ++{ ++ struct spi_transfer_list* list = spi_transfer_desc; ++ ++ list->tx[0] = tx; list->txlen[0] = tx_len; ++ list->rx[0] = rx; list->rxlen[0] = rx_len; ++ ++ list->tx[1] = txnext; list->txlen[1] = txnext_len; ++ list->rx[1] = rxnext; list->rxlen[1] = rxnext_len; ++ ++ list->nr_transfers = nr; ++ ++ return spi_transfer(list); ++} ++ ++/* ......................................................................... */ ++ ++/* ++ * Poll the DataFlash device until it is READY. ++ */ ++void at91_dataflash_waitready(void) ++{ ++ char* command = kmalloc(2, GFP_KERNEL); ++ ++ if (!command) ++ return; ++ ++ do { ++ command[0] = OP_READ_STATUS; ++ command[1] = 0; ++ ++ do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0); ++ } while ((command[1] & 0x80) == 0); ++ ++ kfree(command); ++} ++ ++/* ++ * Return the status of the DataFlash device. ++ */ ++unsigned short at91_dataflash_status(void) ++{ ++ unsigned short status; ++ char* command = kmalloc(2, GFP_KERNEL); ++ ++ if (!command) ++ return 0; ++ ++ command[0] = OP_READ_STATUS; ++ command[1] = 0; ++ ++ do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0); ++ status = command[1]; ++ ++ kfree(command); ++ return status; ++} ++ ++/* ......................................................................... */ ++ ++/* ++ * Erase blocks of flash. ++ */ ++int at91_dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; ++ unsigned int pageaddr; ++ char* command; ++ ++#ifdef DEBUG_DATAFLASH ++ printk("dataflash_erase: addr=%i len=%i\n", instr->addr, instr->len); ++#endif ++ ++ /* Sanity checks */ ++ if (instr->addr + instr->len > mtd->size) ++ return -EINVAL; ++ if ((instr->len % mtd->erasesize != 0) || (instr->len % priv->page_size != 0)) ++ return -EINVAL; ++ if ((instr->addr % priv->page_size) != 0) ++ return -EINVAL; ++ ++ command = kmalloc(4, GFP_KERNEL); ++ if (!command) ++ return -ENOMEM; ++ ++ while (instr->len > 0) { ++ /* Calculate flash page address */ ++ pageaddr = (instr->addr / priv->page_size) << priv->page_offset; ++ ++ command[0] = OP_ERASE_PAGE; ++ command[1] = (pageaddr & 0x00FF0000) >> 16; ++ command[2] = (pageaddr & 0x0000FF00) >> 8; ++ command[3] = 0; ++#ifdef DEBUG_DATAFLASH ++ printk("ERASE: (%x) %x %x %x [%i]\n", command[0], command[1], command[2], command[3], pageaddr); ++#endif ++ ++ /* Send command to SPI device */ ++ spi_access_bus(priv->spi); ++ do_spi_transfer(1, command, 4, command, 4, NULL, 0, NULL, 0); ++ ++ at91_dataflash_waitready(); /* poll status until ready */ ++ spi_release_bus(priv->spi); ++ ++ instr->addr += priv->page_size; /* next page */ ++ instr->len -= priv->page_size; ++ } ++ ++ kfree(command); ++ ++ /* Inform MTD subsystem that erase is complete */ ++ instr->state = MTD_ERASE_DONE; ++ if (instr->callback) ++ instr->callback(instr); ++ ++ return 0; ++} ++ ++/* ++ * Read from the DataFlash device. ++ * from : Start offset in flash device ++ * len : Amount to read ++ * retlen : About of data actually read ++ * buf : Buffer containing the data ++ */ ++int at91_dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) ++{ ++ struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; ++ unsigned int addr; ++ char* command; ++ ++#ifdef DEBUG_DATAFLASH ++ printk("dataflash_read: %lli .. %lli\n", from, from+len); ++#endif ++ ++ *retlen = 0; ++ ++ /* Sanity checks */ ++ if (!len) ++ return 0; ++ if (from + len > mtd->size) ++ return -EINVAL; ++ ++ /* Calculate flash page/byte address */ ++ addr = (((unsigned)from / priv->page_size) << priv->page_offset) + ((unsigned)from % priv->page_size); ++ ++ command = kmalloc(8, GFP_KERNEL); ++ if (!command) ++ return -ENOMEM; ++ ++ command[0] = OP_READ_CONTINUOUS; ++ command[1] = (addr & 0x00FF0000) >> 16; ++ command[2] = (addr & 0x0000FF00) >> 8; ++ command[3] = (addr & 0x000000FF); ++#ifdef DEBUG_DATAFLASH ++ printk("READ: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]); ++#endif ++ ++ /* Send command to SPI device */ ++ spi_access_bus(priv->spi); ++ do_spi_transfer(2, command, 8, command, 8, buf, len, buf, len); ++ spi_release_bus(priv->spi); ++ ++ *retlen = len; ++ kfree(command); ++ return 0; ++} ++ ++/* ++ * Write to the DataFlash device. ++ * to : Start offset in flash device ++ * len : Amount to write ++ * retlen : Amount of data actually written ++ * buf : Buffer containing the data ++ */ ++int at91_dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) ++{ ++ struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; ++ unsigned int pageaddr, addr, offset, writelen; ++ size_t remaining; ++ u_char *writebuf; ++ unsigned short status; ++ int res = 0; ++ char* command; ++ char* tmpbuf = NULL; ++ ++#ifdef DEBUG_DATAFLASH ++ printk("dataflash_write: %lli .. %lli\n", to, to+len); ++#endif ++ ++ *retlen = 0; ++ ++ /* Sanity checks */ ++ if (!len) ++ return 0; ++ if (to + len > mtd->size) ++ return -EINVAL; ++ ++ command = kmalloc(4, GFP_KERNEL); ++ if (!command) ++ return -ENOMEM; ++ ++ pageaddr = ((unsigned)to / priv->page_size); ++ offset = ((unsigned)to % priv->page_size); ++ if (offset + len > priv->page_size) ++ writelen = priv->page_size - offset; ++ else ++ writelen = len; ++ writebuf = buf; ++ remaining = len; ++ ++ /* Allocate temporary buffer */ ++ tmpbuf = kmalloc(priv->page_size, GFP_KERNEL); ++ if (!tmpbuf) { ++ kfree(command); ++ return -ENOMEM; ++ } ++ ++ /* Gain access to the SPI bus */ ++ spi_access_bus(priv->spi); ++ ++ while (remaining > 0) { ++#ifdef DEBUG_DATAFLASH ++ printk("write @ %i:%i len=%i\n", pageaddr, offset, writelen); ++#endif ++ ++ /* (1) Transfer to Buffer1 */ ++ if (writelen != priv->page_size) { ++ addr = pageaddr << priv->page_offset; ++ command[0] = OP_TRANSFER_BUF1; ++ command[1] = (addr & 0x00FF0000) >> 16; ++ command[2] = (addr & 0x0000FF00) >> 8; ++ command[3] = 0; ++#ifdef DEBUG_DATAFLASH ++ printk("TRANSFER: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]); ++#endif ++ do_spi_transfer(1, command, 4, command, 4, NULL, 0, NULL, 0); ++ at91_dataflash_waitready(); ++ } ++ ++ /* (2) Program via Buffer1 */ ++ addr = (pageaddr << priv->page_offset) + offset; ++ command[0] = OP_PROGRAM_VIA_BUF1; ++ command[1] = (addr & 0x00FF0000) >> 16; ++ command[2] = (addr & 0x0000FF00) >> 8; ++ command[3] = (addr & 0x000000FF); ++#ifdef DEBUG_DATAFLASH ++ printk("PROGRAM: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]); ++#endif ++ do_spi_transfer(2, command, 4, command, 4, writebuf, writelen, tmpbuf, writelen); ++ at91_dataflash_waitready(); ++ ++ /* (3) Compare to Buffer1 */ ++ addr = pageaddr << priv->page_offset; ++ command[0] = OP_COMPARE_BUF1; ++ command[1] = (addr & 0x00FF0000) >> 16; ++ command[2] = (addr & 0x0000FF00) >> 8; ++ command[3] = 0; ++#ifdef DEBUG_DATAFLASH ++ printk("COMPARE: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]); ++#endif ++ do_spi_transfer(1, command, 4, command, 4, NULL, 0, NULL, 0); ++ at91_dataflash_waitready(); ++ ++ /* Get result of the compare operation */ ++ status = at91_dataflash_status(); ++ if ((status & 0x40) == 1) { ++ printk("at91_dataflash: Write error on page %i\n", pageaddr); ++ remaining = 0; ++ res = -EIO; ++ } ++ ++ remaining = remaining - writelen; ++ pageaddr++; ++ offset = 0; ++ writebuf += writelen; ++ *retlen += writelen; ++ ++ if (remaining > priv->page_size) ++ writelen = priv->page_size; ++ else ++ writelen = remaining; ++ } ++ ++ /* Release SPI bus */ ++ spi_release_bus(priv->spi); ++ ++ kfree(tmpbuf); ++ kfree(command); ++ return res; ++} ++ ++/* ......................................................................... */ ++ ++/* ++ * Initialize and register DataFlash device with MTD subsystem. ++ */ ++int add_dataflash(int channel, char *name, int size, int pagesize, int pageoffset) ++{ ++ struct mtd_info *device; ++ struct dataflash_local *priv; ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ char mtdID[14]; ++#endif ++ ++ if (nr_devices >= DATAFLASH_MAX_DEVICES) { ++ printk(KERN_ERR "at91_dataflash: Too many devices detected\n"); ++ return 0; ++ } ++ ++ device = (struct mtd_info *) kmalloc(sizeof(struct mtd_info), GFP_KERNEL); ++ if (!device) ++ return -ENOMEM; ++ memset(device, 0, sizeof(struct mtd_info)); ++ ++ device->name = name; ++ device->size = size; ++ device->erasesize = pagesize; ++ device->module = THIS_MODULE; ++ device->type = MTD_NORFLASH; ++ device->flags = MTD_CAP_NORFLASH; ++ device->erase = at91_dataflash_erase; ++ device->read = at91_dataflash_read; ++ device->write = at91_dataflash_write; ++ ++ priv = (struct dataflash_local *) kmalloc(sizeof(struct dataflash_local), GFP_KERNEL); ++ if (!priv) { ++ kfree(device); ++ return -ENOMEM; ++ } ++ memset(priv, 0, sizeof(struct dataflash_local)); ++ ++ priv->spi = channel; ++ priv->page_size = pagesize; ++ priv->page_offset = pageoffset; ++ device->priv = priv; ++ ++ mtd_devices[nr_devices] = device; ++ nr_devices++; ++ printk("at91_dataflash: %s detected [spi%i] (%i bytes)\n", name, channel, size); ++ ++#ifdef CONFIG_MTD_PARTITIONS ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ sprintf(mtdID, "dataflash%i", nr_devices-1); ++ mtd_parts_nr = parse_cmdline_partitions(device, &mtd_parts, mtdID); ++#endif ++ if (mtd_parts_nr <= 0) { ++ mtd_parts = static_partitions; ++ mtd_parts_nr = NB_OF(static_partitions); ++ } ++ ++ return add_mtd_partitions(device, mtd_parts, mtd_parts_nr); ++#else ++ return add_mtd_device(device); ++#endif ++} ++ ++/* ++ * Detect and initialize DataFlash device connected to specified SPI channel. ++ */ ++int at91_dataflash_detect(int channel) ++{ ++ int res = 0; ++ unsigned short status; ++ ++ spi_access_bus(channel); ++ status = at91_dataflash_status(); ++ if (status != 0xff) { /* no dataflash device there */ ++ switch (status & 0x3c) { ++ case 0x2c: /* 1 0 1 1 */ ++ res = add_dataflash(channel, "Atmel AT45DB161B", 4096*528, 528, 10); ++ break; ++ case 0x34: /* 1 1 0 1 */ ++ res = add_dataflash(channel, "Atmel AT45DB321B", 8192*528, 528, 10); ++ break; ++ case 0x3c: /* 1 1 1 1 */ ++ res = add_dataflash(channel, "Atmel AT45DB642", 8192*1056, 1056, 11); ++ break; ++ default: ++ printk(KERN_ERR "at91_dataflash: Unknown device (%x)\n", status & 0x3c); ++ } ++ } ++ spi_release_bus(channel); ++ ++ return res; ++} ++ ++int __init at91_dataflash_init(void) ++{ ++ spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list), GFP_KERNEL); ++ if (!spi_transfer_desc) ++ return -ENOMEM; ++ ++ /* DataFlash (SPI chip select 0) */ ++ at91_dataflash_detect(0); ++ ++#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD ++ /* DataFlash card (SPI chip select 3) */ ++ AT91_CfgPIO_DataFlashCard(); ++ at91_dataflash_detect(3); ++#endif ++ ++ return 0; ++} ++ ++void __exit at91_dataflash_exit(void) ++{ ++ int i; ++ ++ for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) { ++ if (mtd_devices[i]) { ++#ifdef CONFIG_MTD_PARTITIONS ++ del_mtd_partitions(mtd_devices[i]); ++#else ++ del_mtd_device(mtd_devices[i]); ++#endif ++ kfree(mtd_devices[i]->priv); ++ kfree(mtd_devices[i]); ++ } ++ } ++ nr_devices = 0; ++ kfree(spi_transfer_desc); ++} ++ ++ ++EXPORT_NO_SYMBOLS; ++ ++module_init(at91_dataflash_init); ++module_exit(at91_dataflash_exit); ++ ++MODULE_LICENSE("GPL") ++MODULE_AUTHOR("Andrew Victor") ++MODULE_DESCRIPTION("DataFlash driver for Atmel AT91RM9200") +diff -urN linux-2.4.26/drivers/at91/mtd/at91_dataflash.h linux-2.4.26-vrs1/drivers/at91/mtd/at91_dataflash.h +--- linux-2.4.26/drivers/at91/mtd/at91_dataflash.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/mtd/at91_dataflash.h 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,42 @@ ++/* ++ * Atmel DataFlash driver for the Atmel AT91RM9200 (Thunder) ++ * ++ * (c) SAN People (Pty) Ltd ++ * ++ * 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. ++ */ ++ ++#ifndef AT91_DATAFLASH_H ++#define AT91_DATAFLASH_H ++ ++#define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */ ++ ++#define OP_READ_CONTINUOUS 0xE8 ++#define OP_READ_PAGE 0xD2 ++#define OP_READ_BUFFER1 0xD4 ++#define OP_READ_BUFFER2 0xD6 ++#define OP_READ_STATUS 0xD7 ++ ++#define OP_ERASE_PAGE 0x81 ++#define OP_ERASE_BLOCK 0x50 ++ ++#define OP_TRANSFER_BUF1 0x53 ++#define OP_TRANSFER_BUF2 0x55 ++#define OP_COMPARE_BUF1 0x60 ++#define OP_COMPARE_BUF2 0x61 ++ ++#define OP_PROGRAM_VIA_BUF1 0x82 ++#define OP_PROGRAM_VIA_BUF2 0x85 ++ ++struct dataflash_local ++{ ++ int spi; /* SPI chip-select number */ ++ ++ unsigned int page_size; /* number of bytes per page */ ++ unsigned short page_offset; /* page offset in flash address */ ++}; ++ ++#endif +diff -urN linux-2.4.26/drivers/at91/mtd/at91_nand.c linux-2.4.26-vrs1/drivers/at91/mtd/at91_nand.c +--- linux-2.4.26/drivers/at91/mtd/at91_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/mtd/at91_nand.c 2004-03-04 22:11:35.000000000 +0000 +@@ -0,0 +1,328 @@ ++/* ++ * drivers/at91/mtd/at91_nand.c ++ * ++ * Copyright (c) 2003 Rick Bronson ++ * ++ * Derived from drivers/mtd/nand/autcpu12.c ++ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) ++ * ++ * Derived from drivers/mtd/spia.c ++ * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "at91_nand.h" ++ ++/* ++ * MTD structure for AT91 board ++ */ ++static struct mtd_info *at91_mtd = NULL; ++static struct nand_chip *my_nand_chip = NULL; ++ ++static int at91_fio_base; ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ ++/* ++ * Define partitions for flash devices ++ */ ++ ++static struct mtd_partition partition_info32k[] = { ++ { name: "AT91 NAND partition 1, kernel", ++ offset: 0, ++ size: 1 * SZ_1M }, ++ { name: "AT91 NAND partition 2, filesystem", ++ offset: 1 * SZ_1M, ++ size: 16 * SZ_1M }, ++ { name: "AT91 NAND partition 3a, storage", ++ offset: (1 * SZ_1M) + (16 * SZ_1M), ++ size: 1 * SZ_1M }, ++ { name: "AT91 NAND partition 3b, storage", ++ offset: (2 * SZ_1M) + (16 * SZ_1M), ++ size: 1 * SZ_1M }, ++ { name: "AT91 NAND partition 3c, storage", ++ offset: (3 * SZ_1M) + (16 * SZ_1M), ++ size: 1 * SZ_1M }, ++ { name: "AT91 NAND partition 3d, storage", ++ offset: (4 * SZ_1M) + (16 * SZ_1M), ++ size: 1 * SZ_1M }, ++}; ++ ++static struct mtd_partition partition_info64k[] = { ++ { name: "AT91 NAND partition 1, kernel", ++ offset: 0, ++ size: 1 * SZ_1M }, ++ { name: "AT91 NAND partition 2, filesystem", ++ offset: 1 * SZ_1M, ++ size: 16 * SZ_1M }, ++ { name: "AT91 NAND partition 3, storage", ++ offset: (1 * SZ_1M) + (16 * SZ_1M), ++ size: 47 * SZ_1M }, ++}; ++ ++#endif ++ ++/* ++ * Hardware specific access to control-lines ++ */ ++static void at91_hwcontrol(int cmd) ++{ ++ struct nand_chip *my_nand = my_nand_chip; ++ switch(cmd) ++ { ++ case NAND_CTL_SETCLE: ++ my_nand->IO_ADDR_W = at91_fio_base + AT91_SMART_MEDIA_CLE; ++ break; ++ case NAND_CTL_CLRCLE: ++ my_nand->IO_ADDR_W = at91_fio_base; ++ break; ++ case NAND_CTL_SETALE: ++ my_nand->IO_ADDR_W = at91_fio_base + AT91_SMART_MEDIA_ALE; ++ break; ++ case NAND_CTL_CLRALE: ++ my_nand->IO_ADDR_W = at91_fio_base; ++ break; ++ case NAND_CTL_SETNCE: ++ break; ++ case NAND_CTL_CLRNCE: ++ break; ++ } ++} ++ ++/* ++ * Send command to NAND device ++ */ ++static void at91_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) ++{ ++ register struct nand_chip *my_nand = mtd->priv; ++ ++ /* Begin command latch cycle */ ++ register unsigned long NAND_IO_ADDR = my_nand->IO_ADDR_W + AT91_SMART_MEDIA_CLE; ++ ++ /* ++ * Write out the command to the device. ++ */ ++ if (command != NAND_CMD_SEQIN) ++ writeb (command, NAND_IO_ADDR); ++ else { ++ if (mtd->oobblock == 256 && column >= 256) { ++ column -= 256; ++ writeb (NAND_CMD_RESET, NAND_IO_ADDR); ++ writeb (NAND_CMD_READOOB, NAND_IO_ADDR); ++ writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); ++ } ++ else ++ if (mtd->oobblock == 512 && column >= 256) { ++ if (column < 512) { ++ column -= 256; ++ writeb (NAND_CMD_READ1, NAND_IO_ADDR); ++ writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); ++ } else { ++ column -= 512; ++ writeb (NAND_CMD_READOOB, NAND_IO_ADDR); ++ writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); ++ } ++ } else { ++ writeb (NAND_CMD_READ0, NAND_IO_ADDR); ++ writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); ++ } ++ } ++ ++ /* Set ALE and clear CLE to start address cycle */ ++ NAND_IO_ADDR = at91_fio_base; ++ ++ if (column != -1 || page_addr != -1) ++ NAND_IO_ADDR += AT91_SMART_MEDIA_ALE; ++ ++ /* Serially input address */ ++ if (column != -1) ++ writeb (column, NAND_IO_ADDR); ++ if (page_addr != -1) { ++ writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR); ++ writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR); ++ /* One more address cycle for higher density devices */ ++ if (mtd->size & 0x0c000000) { ++ writeb ((unsigned char) ((page_addr >> 16) & 0x0f), NAND_IO_ADDR); ++ } ++ } ++ ++ /* wait until command is processed */ ++ while (!my_nand->dev_ready()) ++ ; ++} ++ ++/* ++ * Read the Device Ready pin. ++ */ ++static int at91_device_ready(void) ++{ ++ return AT91_PIO_SmartMedia_RDY(); ++} ++/* ++ * Main initialization routine ++ */ ++static int __init at91_init (void) ++{ ++ struct nand_chip *my_nand; ++ int err = 0; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ at91_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); ++ if (!at91_mtd) { ++ printk ("Unable to allocate AT91 NAND MTD device structure.\n"); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* map physical adress */ ++ at91_fio_base = (unsigned long) ioremap(AT91_SMARTMEDIA_BASE, SZ_8M); ++ if(!at91_fio_base) { ++ printk("ioremap AT91 NAND failed\n"); ++ err = -EIO; ++ goto out_mtd; ++ } ++ ++ /* Get pointer to private data */ ++ my_nand_chip = my_nand = (struct nand_chip *) (&at91_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) at91_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) my_nand, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ at91_mtd->priv = my_nand; ++ ++ /* Set address of NAND IO lines */ ++ my_nand->IO_ADDR_R = at91_fio_base; ++ my_nand->IO_ADDR_W = at91_fio_base; ++ my_nand->hwcontrol = at91_hwcontrol; ++ my_nand->dev_ready = at91_device_ready; ++ my_nand->cmdfunc = at91_nand_command; /* we need our own */ ++ my_nand->eccmode = NAND_ECC_SOFT; /* enable ECC */ ++ /* 20 us command delay time */ ++ my_nand->chip_delay = 20; ++ ++ /* Setup Smart Media, first enable the address range of CS3 */ ++ AT91_SYS->EBI_CSA |= AT91C_EBI_CS3A_SMC_SmartMedia; ++ /* set the bus interface characteristics based on ++ tDS Data Set up Time 30 - ns ++ tDH Data Hold Time 20 - ns ++ tALS ALE Set up Time 20 - ns ++ 16ns at 60 MHz ~= 3 */ ++#define AT91C_SM_ID_RWH (5 << 28) /* orig = 5 */ ++#define AT91C_SM_RWH (1 << 28) /* orig = 1 */ ++#define AT91C_SM_RWS (0 << 24) /* orig = 0 */ ++#define AT91C_SM_TDF (1 << 8) /* orig = 1 */ ++#define AT91C_SM_NWS (5) /* orig = 3 */ ++ AT91_SYS->EBI_SMC2_CSR[3] = ( AT91C_SM_RWH | AT91C_SM_RWS | ++ AT91C_SMC2_ACSS_STANDARD | ++ AT91C_SMC2_DBW_8 | AT91C_SM_TDF | ++ AT91C_SMC2_WSEN | AT91C_SM_NWS); ++ ++ AT91_CfgPIO_SmartMedia(); ++ ++ if (AT91_PIO_SmartMedia_CardDetect()) ++ printk ("No "); ++ printk ("SmartMedia card inserted.\n"); ++ ++ /* Scan to find existance of the device */ ++ if (nand_scan (at91_mtd)) { ++ err = -ENXIO; ++ goto out_ior; ++ } ++ ++ /* Allocate memory for internal data buffer */ ++ my_nand->data_buf = kmalloc (sizeof(u_char) * (at91_mtd->oobblock + at91_mtd->oobsize), GFP_KERNEL); ++ if (!my_nand->data_buf) { ++ printk ("Unable to allocate AT91 NAND data buffer.\n"); ++ err = -ENOMEM; ++ goto out_ior; ++ } ++ ++ /* Allocate memory for internal data buffer */ ++ my_nand->data_cache = kmalloc (sizeof(u_char) * (at91_mtd->oobblock + at91_mtd->oobsize), GFP_KERNEL); ++ if (!my_nand->data_cache) { ++ printk ("Unable to allocate AT91 NAND data cache.\n"); ++ err = -ENOMEM; ++ goto out_buf; ++ } ++ my_nand->cache_page = -1; ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ /* Register the partitions */ ++ switch(at91_mtd->size) ++ { ++ case SZ_32M: ++ err = add_mtd_partitions(at91_mtd, partition_info32k, ++ ARRAY_SIZE (partition_info32k)); ++ break; ++ case SZ_64M: ++ err = add_mtd_partitions(at91_mtd, partition_info64k, ++ ARRAY_SIZE (partition_info64k)); ++ break; ++ default: ++ printk ("Unsupported SmartMedia device\n"); ++ err = -ENXIO; ++ goto out_cac; ++ } ++#else ++ err = add_mtd_device(at91_mtd); ++#endif ++ goto out; ++ ++ out_cac: ++ kfree (my_nand->data_cache); ++ out_buf: ++ kfree (my_nand->data_buf); ++ out_ior: ++ iounmap((void *)at91_fio_base); ++ out_mtd: ++ kfree (at91_mtd); ++ out: ++ return err; ++} ++ ++/* ++ * Clean up routine ++ */ ++static void __exit at91_cleanup (void) ++{ ++ struct nand_chip *my_nand = (struct nand_chip *) &at91_mtd[1]; ++ ++ /* Unregister partitions */ ++ del_mtd_partitions(at91_mtd); ++ ++ /* Unregister the device */ ++ del_mtd_device (at91_mtd); ++ ++ /* Free internal data buffers */ ++ kfree (my_nand->data_buf); ++ kfree (my_nand->data_cache); ++ ++ /* unmap physical adress */ ++ iounmap((void *)at91_fio_base); ++ ++ /* Free the MTD device structure */ ++ kfree (at91_mtd); ++} ++ ++module_init(at91_init); ++module_exit(at91_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Rick Bronson"); ++MODULE_DESCRIPTION("Glue layer for SmartMediaCard on ATMEL AT91RM9200"); +diff -urN linux-2.4.26/drivers/at91/mtd/at91_nand.h linux-2.4.26-vrs1/drivers/at91/mtd/at91_nand.h +--- linux-2.4.26/drivers/at91/mtd/at91_nand.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/mtd/at91_nand.h 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,27 @@ ++/* ++ * AT91RM9200 specific NAND (SmartMedia) defines ++ * ++ * (c) 2003 Rick Bronson ++ * ++ * 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 ++ */ ++ ++#ifndef __AT91_NAND_H ++#define __AT91_NAND_H ++ ++#define AT91_SMART_MEDIA_ALE (1 << 22) /* our ALE is AD22 */ ++#define AT91_SMART_MEDIA_CLE (1 << 21) /* our CLE is AD21 */ ++ ++#endif +diff -urN linux-2.4.26/drivers/at91/net/Makefile linux-2.4.26-vrs1/drivers/at91/net/Makefile +--- linux-2.4.26/drivers/at91/net/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/net/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,15 @@ ++# File: drivers/at91/net/Makefile ++# ++# Makefile for the Atmel AT91RM9200 ethernet device drivers ++# ++ ++O_TARGET := at91net.o ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++obj-$(CONFIG_AT91_ETHER) += at91_ether.o ++ ++include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/at91/net/at91_ether.c linux-2.4.26-vrs1/drivers/at91/net/at91_ether.c +--- linux-2.4.26/drivers/at91/net/at91_ether.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/net/at91_ether.c 2004-03-04 22:07:29.000000000 +0000 +@@ -0,0 +1,877 @@ ++/* ++ * Ethernet driver for the Atmel AT91RM9200 (Thunder) ++ * ++ * (c) SAN People (Pty) Ltd ++ * ++ * Based on an earlier Atmel EMAC macrocell driver by Atmel and Lineo Inc. ++ * Initial version by Rick Bronson 01/11/2003 ++ * ++ * Intel LXT971A PHY support by Christopher Bahns & David Knickerbocker ++ * (Polaroid Corporation) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "at91_ether.h" ++ ++static struct net_device at91_dev; ++ ++/* ........................... PHY INTERFACE ........................... */ ++ ++/* ++ * Enable the MDIO bit in MAC control register ++ * When not called from an interrupt-handler, access to the PHY must be ++ * protected by a spinlock. ++ */ ++static void enable_mdi(AT91PS_EMAC regs) ++{ ++ regs->EMAC_CTL |= AT91C_EMAC_MPE; /* enable management port */ ++} ++ ++/* ++ * Disable the MDIO bit in the MAC control register ++ */ ++static void disable_mdi(AT91PS_EMAC regs) ++{ ++ regs->EMAC_CTL &= ~AT91C_EMAC_MPE; /* disable management port */ ++} ++ ++/* ++ * Write value to the a PHY register ++ * Note: MDI interface is assumed to already have been enabled. ++ */ ++static void write_phy(AT91PS_EMAC regs, unsigned char phy_addr, unsigned char address, unsigned int value) ++{ ++ regs->EMAC_MAN = (AT91C_EMAC_HIGH | AT91C_EMAC_CODE_802_3 | AT91C_EMAC_RW_W ++ | ((phy_addr & 0x1f) << 23) | (address << 18)) + (value & 0xffff); ++ ++ /* Wait until IDLE bit in Network Status register is cleared */ ++ // TODO: Enforce some maximum loop-count? ++ while (!(regs->EMAC_SR & AT91C_EMAC_IDLE)) { barrier(); } ++} ++ ++/* ++ * Read value stored in a PHY register. ++ * Note: MDI interface is assumed to already have been enabled. ++ */ ++static void read_phy(AT91PS_EMAC regs, unsigned char phy_addr, unsigned char address, unsigned int *value) ++{ ++ regs->EMAC_MAN = AT91C_EMAC_HIGH | AT91C_EMAC_CODE_802_3 | AT91C_EMAC_RW_R ++ | ((phy_addr & 0x1f) << 23) | (address << 18); ++ ++ /* Wait until IDLE bit in Network Status register is cleared */ ++ // TODO: Enforce some maximum loop-count? ++ while (!(regs->EMAC_SR & AT91C_EMAC_IDLE)) { barrier(); } ++ ++ *value = (regs->EMAC_MAN & 0x0000ffff); ++} ++ ++/* ........................... PHY MANAGEMENT .......................... */ ++ ++/* ++ * Access the PHY to determine the current Link speed and Mode, and update the ++ * MAC accordingly. ++ * If no link or auto-negotiation is busy, then no changes are made. ++ * Returns: 0 : OK ++ * -1 : No link ++ * -2 : AutoNegotiation still in progress ++ */ ++static int update_linkspeed(struct net_device *dev, AT91PS_EMAC regs) { ++ unsigned int bmsr, bmcr, lpa, mac_cfg; ++ unsigned int speed, duplex; ++ ++ /* Link status is latched, so read twice to get current value */ ++ read_phy(regs, 0, MII_BMSR, &bmsr); ++ read_phy(regs, 0, MII_BMSR, &bmsr); ++ if (!(bmsr & BMSR_LSTATUS)) return -1; /* no link */ ++ ++ read_phy(regs, 0, MII_BMCR, &bmcr); ++ if (bmcr & BMCR_ANENABLE) { /* AutoNegotiation is enabled */ ++ if (!(bmsr & BMSR_ANEGCOMPLETE)) return -2; /* auto-negotitation in progress */ ++ ++ read_phy(regs, 0, MII_LPA, &lpa); ++ if ((lpa & LPA_100FULL) || (lpa & LPA_100HALF)) speed = SPEED_100; ++ else speed = SPEED_10; ++ if ((lpa & LPA_100FULL) || (lpa & LPA_10FULL)) duplex = DUPLEX_FULL; ++ else duplex = DUPLEX_HALF; ++ } else { ++ speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10; ++ duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; ++ } ++ ++ /* Update the MAC */ ++ mac_cfg = regs->EMAC_CFG & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD); ++ if (speed == SPEED_100) { ++ if (duplex == DUPLEX_FULL) /* 100 Full Duplex */ ++ regs->EMAC_CFG = mac_cfg | AT91C_EMAC_SPD | AT91C_EMAC_FD; ++ else /* 100 Half Duplex */ ++ regs->EMAC_CFG = mac_cfg | AT91C_EMAC_SPD; ++ } else { ++ if (duplex == DUPLEX_FULL) /* 10 Full Duplex */ ++ regs->EMAC_CFG = mac_cfg | AT91C_EMAC_FD; ++ else /* 10 Half Duplex */ ++ regs->EMAC_CFG = mac_cfg; ++ } ++ ++ printk(KERN_INFO "%s: Link now %i-%s\n", dev->name, speed, (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex"); ++ return 0; ++} ++ ++/* ++ * Handle interrupts from the PHY ++ */ ++static void at91ether_phy_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct net_device *dev = (struct net_device *) dev_id; ++ struct at91_private *lp = (struct at91_private *) dev->priv; ++ AT91PS_EMAC emac = (AT91PS_EMAC) dev->base_addr; ++ int status; ++ unsigned int phy; ++ ++ enable_mdi(emac); ++ if (lp->phy_type == MII_DM9161_ID) ++ read_phy(emac, 0, MII_DSINTR_REG, &phy); /* ack interrupt in Davicom PHY */ ++ else if (lp->phy_type == MII_LXT971A_ID) ++ read_phy(emac, 0, MII_ISINTS_REG, &phy); /* ack interrupt in Intel PHY */ ++ ++ status = AT91_SYS->PIOC_ISR; /* acknowledge interrupt in PIO */ ++ ++ status = update_linkspeed(dev, emac); ++ if (status == -1) { /* link is down */ ++ netif_carrier_off(dev); ++ printk(KERN_INFO "%s: Link down.\n", dev->name); ++ } else if (status == -2) { /* auto-negotiation in progress */ ++ /* Do nothing - another interrupt generated when negotiation complete */ ++ } else { /* link is operational */ ++ netif_carrier_on(dev); ++ } ++ disable_mdi(emac); ++} ++ ++/* ++ * Initialize and enable the PHY interrupt when link-state changes ++ */ ++static void enable_phyirq(struct net_device *dev, AT91PS_EMAC regs) ++{ ++ struct at91_private *lp = (struct at91_private *) dev->priv; ++ unsigned int dsintr, status; ++ ++ // TODO: Check error code. Really need a generic PIO (interrupt) ++ // layer since we're really only interested in the PC4 (DK) or PC2 (CSB337) line. ++ (void) request_irq(AT91C_ID_PIOC, at91ether_phy_interrupt, 0, dev->name, dev); ++ ++ status = AT91_SYS->PIOC_ISR; /* clear any pending PIO interrupts */ ++#ifdef CONFIG_MACH_CSB337 ++ AT91_SYS->PIOC_IER = AT91C_PIO_PC2; /* Enable interrupt */ ++#else ++ AT91_SYS->PIOC_IER = AT91C_PIO_PC4; /* Enable interrupt */ ++#endif ++ ++ spin_lock_irq(&lp->lock); ++ enable_mdi(regs); ++ ++ if (lp->phy_type == MII_DM9161_ID) { /* for Davicom PHY */ ++ read_phy(regs, 0, MII_DSINTR_REG, &dsintr); ++ dsintr = dsintr & ~0xf00; /* clear bits 8..11 */ ++ write_phy(regs, 0, MII_DSINTR_REG, dsintr); ++ } ++ else if (lp->phy_type == MII_LXT971A_ID) { /* for Intel PHY */ ++ read_phy(regs, 0, MII_ISINTE_REG, &dsintr); ++ dsintr = dsintr | 0xf2; /* set bits 1, 4..7 */ ++ write_phy(regs, 0, MII_ISINTE_REG, dsintr); ++ } ++ ++ disable_mdi(regs); ++ spin_unlock_irq(&lp->lock); ++} ++ ++/* ++ * Disable the PHY interrupt ++ */ ++static void disable_phyirq(struct net_device *dev, AT91PS_EMAC regs) ++{ ++ struct at91_private *lp = (struct at91_private *) dev->priv; ++ unsigned int dsintr; ++ ++ spin_lock_irq(&lp->lock); ++ enable_mdi(regs); ++ ++ if (lp->phy_type == MII_DM9161_ID) { /* for Davicom PHY */ ++ read_phy(regs, 0, MII_DSINTR_REG, &dsintr); ++ dsintr = dsintr | 0xf00; /* set bits 8..11 */ ++ write_phy(regs, 0, MII_DSINTR_REG, dsintr); ++ } ++ else if (lp->phy_type == MII_LXT971A_ID) { /* for Intel PHY */ ++ read_phy(regs, 0, MII_ISINTE_REG, &dsintr); ++ dsintr = dsintr & ~0xf2; /* clear bits 1, 4..7 */ ++ write_phy(regs, 0, MII_ISINTE_REG, dsintr); ++ } ++ ++ disable_mdi(regs); ++ spin_unlock_irq(&lp->lock); ++ ++#ifdef CONFIG_MACH_CSB337 ++ AT91_SYS->PIOC_IDR = AT91C_PIO_PC2; /* Disable interrupt */ ++#else ++ AT91_SYS->PIOC_IDR = AT91C_PIO_PC4; /* Disable interrupt */ ++#endif ++ free_irq(AT91C_ID_PIOC, dev); /* Free interrupt handler */ ++} ++ ++/* ++ * Perform a software reset of the PHY. ++ */ ++static void reset_phy(struct net_device *dev, AT91PS_EMAC regs) ++{ ++ struct at91_private *lp = (struct at91_private *) dev->priv; ++ unsigned int bmcr; ++ ++ spin_lock_irq(&lp->lock); ++ enable_mdi(regs); ++ ++ /* Perform PHY reset */ ++ write_phy(regs, 0, MII_BMCR, BMCR_RESET); ++ ++ /* Wait until PHY reset is complete */ ++ do { ++ read_phy(regs, 0, MII_BMCR, &bmcr); ++ } while (!(bmcr && BMCR_RESET)); ++ ++ disable_mdi(regs); ++ spin_unlock_irq(&lp->lock); ++} ++ ++ ++/* ......................... ADDRESS MANAGEMENT ........................ */ ++ ++/* ++ * Set the ethernet MAC address in dev->dev_addr ++ */ ++static void get_mac_address(struct net_device *dev) { ++ AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; ++ char addr[6]; ++ unsigned int hi, lo; ++ ++ /* Check if bootloader set address in Specific-Address 1 */ ++ hi = regs->EMAC_SA1H; ++ lo = regs->EMAC_SA1L; ++ addr[0] = (lo & 0xff); ++ addr[1] = (lo & 0xff00) >> 8; ++ addr[2] = (lo & 0xff0000) >> 16; ++ addr[3] = (lo & 0xff000000) >> 24; ++ addr[4] = (hi & 0xff); ++ addr[5] = (hi & 0xff00) >> 8; ++ ++ if (is_valid_ether_addr(addr)) { ++ memcpy(dev->dev_addr, &addr, 6); ++ return; ++ } ++ ++ /* Check if bootloader set address in Specific-Address 2 */ ++ hi = regs->EMAC_SA2H; ++ lo = regs->EMAC_SA2L; ++ addr[0] = (lo & 0xff); ++ addr[1] = (lo & 0xff00) >> 8; ++ addr[2] = (lo & 0xff0000) >> 16; ++ addr[3] = (lo & 0xff000000) >> 24; ++ addr[4] = (hi & 0xff); ++ addr[5] = (hi & 0xff00) >> 8; ++ ++ if (is_valid_ether_addr(addr)) { ++ memcpy(dev->dev_addr, &addr, 6); ++ return; ++ } ++} ++ ++/* ++ * Program the hardware MAC address from dev->dev_addr. ++ */ ++static void update_mac_address(struct net_device *dev) ++{ ++ AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; ++ ++ regs->EMAC_SA1L = (dev->dev_addr[3] << 24) | (dev->dev_addr[2] << 16) | (dev->dev_addr[1] << 8) | (dev->dev_addr[0]); ++ regs->EMAC_SA1H = (dev->dev_addr[5] << 8) | (dev->dev_addr[4]); ++} ++ ++#ifdef AT91_ETHER_ADDR_CONFIGURABLE ++/* ++ * Store the new hardware address in dev->dev_addr, and update the MAC. ++ */ ++static int set_mac_address(struct net_device *dev, void* addr) ++{ ++ struct sockaddr *address = addr; ++ ++ if (!is_valid_ether_addr(address->sa_data)) ++ return -EADDRNOTAVAIL; ++ ++ memcpy(dev->dev_addr, address->sa_data, dev->addr_len); ++ update_mac_address(dev); ++ ++ printk("%s: Setting MAC address to %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, ++ dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], ++ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); ++ ++ return 0; ++} ++#endif ++ ++/* ++ * Add multicast addresses to the internal multicast-hash table. ++ */ ++static void at91ether_sethashtable(struct net_device *dev, AT91PS_EMAC regs) ++{ ++ struct dev_mc_list *curr; ++ unsigned char mc_filter[2]; ++ unsigned int i, bitnr; ++ ++ mc_filter[0] = mc_filter[1] = 0; ++ ++ curr = dev->mc_list; ++ for (i = 0; i < dev->mc_count; i++, curr = curr->next) { ++ if (!curr) break; /* unexpected end of list */ ++ ++ bitnr = ether_crc(ETH_ALEN, curr->dmi_addr) >> 26; ++ mc_filter[bitnr >> 5] |= 1 << (bitnr & 31); ++ } ++ ++ regs->EMAC_HSH = mc_filter[1]; ++ regs->EMAC_HSL = mc_filter[0]; ++} ++ ++/* ++ * Enable/Disable promiscuous and multicast modes. ++ */ ++static void at91ether_set_rx_mode(struct net_device *dev) ++{ ++ AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; ++ ++ if (dev->flags & IFF_PROMISC) { /* Enable promiscuous mode */ ++ regs->EMAC_CFG |= AT91C_EMAC_CAF; ++ } else if (dev->flags & (~IFF_PROMISC)) { /* Disable promiscuous mode */ ++ regs->EMAC_CFG &= ~AT91C_EMAC_CAF; ++ } ++ ++ if (dev->flags & IFF_ALLMULTI) { /* Enable all multicast mode */ ++ regs->EMAC_HSH = -1; ++ regs->EMAC_HSL = -1; ++ regs->EMAC_CFG |= AT91C_EMAC_MTI; ++ } else if (dev->mc_count > 0) { /* Enable specific multicasts */ ++ at91ether_sethashtable(dev, regs); ++ regs->EMAC_CFG |= AT91C_EMAC_MTI; ++ } else if (dev->flags & (~IFF_ALLMULTI)) { /* Disable all multicast mode */ ++ regs->EMAC_HSH = 0; ++ regs->EMAC_HSL = 0; ++ regs->EMAC_CFG &= ~AT91C_EMAC_MTI; ++ } ++} ++ ++/* ............................... IOCTL ............................... */ ++ ++static int mdio_read(struct net_device *dev, int phy_id, int location) ++{ ++ AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; ++ unsigned int value; ++ ++ read_phy(regs, phy_id, location, &value); ++ return value; ++} ++ ++static void mdio_write(struct net_device *dev, int phy_id, int location, int value) ++{ ++ AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; ++ ++ write_phy(regs, phy_id, location, value); ++} ++ ++/* ++ * ethtool support. ++ */ ++static int at91ether_ethtool_ioctl (struct net_device *dev, void *useraddr) ++{ ++ struct at91_private *lp = (struct at91_private *) dev->priv; ++ AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; ++ u32 ethcmd; ++ int res = 0; ++ ++ if (copy_from_user (ðcmd, useraddr, sizeof (ethcmd))) ++ return -EFAULT; ++ ++ spin_lock_irq(&lp->lock); ++ enable_mdi(regs); ++ ++ switch (ethcmd) { ++ case ETHTOOL_GSET: { ++ struct ethtool_cmd ecmd = { ETHTOOL_GSET }; ++ res = mii_ethtool_gset(&lp->mii, &ecmd); ++ if (lp->phy_media == PORT_FIBRE) { /* override media type since mii.c doesn't know */ ++ ecmd.supported = SUPPORTED_FIBRE; ++ ecmd.port = PORT_FIBRE; ++ } ++ if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) ++ res = -EFAULT; ++ break; ++ } ++ case ETHTOOL_SSET: { ++ struct ethtool_cmd ecmd; ++ if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) ++ res = -EFAULT; ++ else ++ res = mii_ethtool_sset(&lp->mii, &ecmd); ++ break; ++ } ++ case ETHTOOL_NWAY_RST: { ++ res = mii_nway_restart(&lp->mii); ++ break; ++ } ++ case ETHTOOL_GLINK: { ++ struct ethtool_value edata = { ETHTOOL_GLINK }; ++ edata.data = mii_link_ok(&lp->mii); ++ if (copy_to_user(useraddr, &edata, sizeof(edata))) ++ res = -EFAULT; ++ break; ++ } ++ default: ++ res = -EOPNOTSUPP; ++ } ++ ++ disable_mdi(regs); ++ spin_unlock_irq(&lp->lock); ++ ++ return res; ++} ++ ++/* ++ * User-space ioctl interface. ++ */ ++static int at91ether_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++ switch(cmd) { ++ case SIOCETHTOOL: ++ return at91ether_ethtool_ioctl(dev, (void *) rq->ifr_data); ++ default: ++ return -EOPNOTSUPP; ++ } ++} ++ ++/* ................................ MAC ................................ */ ++ ++/* ++ * Initialize and start the Receiver and Transmit subsystems ++ */ ++static void at91ether_start(struct net_device *dev) ++{ ++ AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; ++ struct at91_private *lp = (struct at91_private *) dev->priv; ++ int i; ++ struct recv_desc_bufs *dlist, *dlist_phys; ++ ++ dlist = lp->dlist; ++ dlist_phys = lp->dlist_phys; ++ ++ for (i = 0; i < MAX_RX_DESCR; i++) { ++ dlist->descriptors[i].addr = (unsigned int) &dlist_phys->recv_buf[i][0]; ++ dlist->descriptors[i].size = 0; ++ } ++ ++ /* Set the Wrap bit on the last descriptor */ ++ dlist->descriptors[i-1].addr |= EMAC_DESC_WRAP; ++ ++ /* Reset buffer index */ ++ lp->rxBuffIndex = 0; ++ ++ /* Program address of descriptor list in Rx Buffer Queue register */ ++ regs->EMAC_RBQP = (AT91_REG) dlist_phys; ++ ++ /* Enable Receive and Transmit */ ++ regs->EMAC_CTL |= (AT91C_EMAC_RE | AT91C_EMAC_TE); ++} ++ ++/* ++ * Open the ethernet interface ++ */ ++static int at91ether_open(struct net_device *dev) ++{ ++ struct at91_private *lp = (struct at91_private *) dev->priv; ++ AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; ++ ++ if (!is_valid_ether_addr(dev->dev_addr)) ++ return -EADDRNOTAVAIL; ++ ++ AT91_SYS->PMC_PCER = 1 << AT91C_ID_EMAC; /* Re-enable Peripheral clock */ ++ regs->EMAC_CTL |= AT91C_EMAC_CSR; /* Clear internal statistics */ ++ ++ /* Enable PHY interrupt */ ++ enable_phyirq(dev, regs); ++ ++ /* Enable MAC interrupts */ ++ regs->EMAC_IER = AT91C_EMAC_RCOM | AT91C_EMAC_RBNA ++ | AT91C_EMAC_TUND | AT91C_EMAC_RTRY | AT91C_EMAC_TCOM ++ | AT91C_EMAC_ROVR | AT91C_EMAC_HRESP; ++ ++ /* Determine current link speed */ ++ spin_lock_irq(&lp->lock); ++ enable_mdi(regs); ++ (void) update_linkspeed(dev, regs); ++ disable_mdi(regs); ++ spin_unlock_irq(&lp->lock); ++ ++ at91ether_start(dev); ++ netif_start_queue(dev); ++ return 0; ++} ++ ++/* ++ * Close the interface ++ */ ++static int at91ether_close(struct net_device *dev) ++{ ++ AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; ++ ++ /* Disable Receiver and Transmitter */ ++ regs->EMAC_CTL &= ~(AT91C_EMAC_TE | AT91C_EMAC_RE); ++ ++ /* Disable PHY interrupt */ ++ disable_phyirq(dev, regs); ++ ++ /* Disable MAC interrupts */ ++ regs->EMAC_IDR = AT91C_EMAC_RCOM | AT91C_EMAC_RBNA ++ | AT91C_EMAC_TUND | AT91C_EMAC_RTRY | AT91C_EMAC_TCOM ++ | AT91C_EMAC_ROVR | AT91C_EMAC_HRESP; ++ ++ netif_stop_queue(dev); ++ ++ AT91_SYS->PMC_PCDR = 1 << AT91C_ID_EMAC; /* Disable Peripheral clock */ ++ ++ return 0; ++} ++ ++/* ++ * Transmit packet. ++ */ ++static int at91ether_tx(struct sk_buff *skb, struct net_device *dev) ++{ ++ AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; ++ struct at91_private *lp = (struct at91_private *) dev->priv; ++ ++ if (regs->EMAC_TSR & AT91C_EMAC_BNQ) { ++ netif_stop_queue(dev); ++ ++ /* Store packet information (to free when Tx completed) */ ++ lp->skb = skb; ++ lp->skb_length = skb->len; ++ lp->skb_physaddr = pci_map_single(NULL, skb->data, skb->len, PCI_DMA_TODEVICE); ++ lp->stats.tx_bytes += skb->len; ++ ++ /* Set address of the data in the Transmit Address register */ ++ regs->EMAC_TAR = lp->skb_physaddr; ++ /* Set length of the packet in the Transmit Control register */ ++ regs->EMAC_TCR = skb->len; ++ ++ dev->trans_start = jiffies; ++ } else { ++ printk(KERN_ERR "at91_ether.c: at91ether_tx() called, but device is busy!\n"); ++ return 1; /* if we return anything but zero, dev.c:1055 calls kfree_skb(skb) ++ on this skb, he also reports -ENETDOWN and printk's, so either ++ we free and return(0) or don't free and return 1 */ ++ } ++ ++ return 0; ++} ++ ++/* ++ * Update the current statistics from the internal statistics registers. ++ */ ++static struct net_device_stats *at91ether_stats(struct net_device *dev) ++{ ++ struct at91_private *lp = (struct at91_private *) dev->priv; ++ AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr; ++ int ale, lenerr, seqe, lcol, ecol; ++ ++ if (netif_running(dev)) { ++ lp->stats.rx_packets += regs->EMAC_OK; /* Good frames received */ ++ ale = regs->EMAC_ALE; ++ lp->stats.rx_frame_errors += ale; /* Alignment errors */ ++ lenerr = regs->EMAC_ELR + regs->EMAC_USF; ++ lp->stats.rx_length_errors += lenerr; /* Excessive Length or Undersize Frame error */ ++ seqe = regs->EMAC_SEQE; ++ lp->stats.rx_crc_errors += seqe; /* CRC error */ ++ lp->stats.rx_fifo_errors += regs->EMAC_DRFC; /* Receive buffer not available */ ++ lp->stats.rx_errors += (ale + lenerr + seqe + regs->EMAC_CDE + regs->EMAC_RJB); ++ ++ lp->stats.tx_packets += regs->EMAC_FRA; /* Frames successfully transmitted */ ++ lp->stats.tx_fifo_errors += regs->EMAC_TUE; /* Transmit FIFO underruns */ ++ lp->stats.tx_carrier_errors += regs->EMAC_CSE; /* Carrier Sense errors */ ++ lp->stats.tx_heartbeat_errors += regs->EMAC_SQEE; /* Heartbeat error */ ++ ++ lcol = regs->EMAC_LCOL; ++ ecol = regs->EMAC_ECOL; ++ lp->stats.tx_window_errors += lcol; /* Late collisions */ ++ lp->stats.tx_aborted_errors += ecol; /* 16 collisions */ ++ ++ lp->stats.collisions += (regs->EMAC_SCOL + regs->EMAC_MCOL + lcol + ecol); ++ } ++ return &lp->stats; ++} ++ ++/* ++ * Extract received frame from buffer descriptors and sent to upper layers. ++ * (Called from interrupt context) ++ */ ++static void at91ether_rx(struct net_device *dev) ++{ ++ struct at91_private *lp = (struct at91_private *) dev->priv; ++ struct recv_desc_bufs *dlist; ++ unsigned char *p_recv; ++ struct sk_buff *skb; ++ unsigned int pktlen; ++ ++ dlist = lp->dlist; ++ while (dlist->descriptors[lp->rxBuffIndex].addr & EMAC_DESC_DONE) { ++ p_recv = dlist->recv_buf[lp->rxBuffIndex]; ++ pktlen = dlist->descriptors[lp->rxBuffIndex].size & 0x7ff; /* Length of frame including FCS */ ++ skb = alloc_skb(pktlen + 2, GFP_ATOMIC); ++ if (skb != NULL) { ++ skb_reserve(skb, 2); ++ memcpy(skb_put(skb, pktlen), p_recv, pktlen); ++ ++ skb->dev = dev; ++ skb->protocol = eth_type_trans(skb, dev); ++ skb->len = pktlen; ++ dev->last_rx = jiffies; ++ lp->stats.rx_bytes += pktlen; ++ netif_rx(skb); ++ } ++ else { ++ lp->stats.rx_dropped += 1; ++ printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); ++ } ++ ++ if (dlist->descriptors[lp->rxBuffIndex].size & EMAC_MULTICAST) ++ lp->stats.multicast++; ++ ++ dlist->descriptors[lp->rxBuffIndex].addr &= ~EMAC_DESC_DONE; /* reset ownership bit */ ++ if (lp->rxBuffIndex == MAX_RX_DESCR-1) /* wrap after last buffer */ ++ lp->rxBuffIndex = 0; ++ else ++ lp->rxBuffIndex++; ++ } ++} ++ ++/* ++ * MAC interrupt handler ++ */ ++static void at91ether_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct net_device *dev = (struct net_device *) dev_id; ++ struct at91_private *lp = (struct at91_private *) dev->priv; ++ AT91PS_EMAC emac = (AT91PS_EMAC) dev->base_addr; ++ unsigned long intstatus; ++ ++ /* MAC Interrupt Status register indicates what interrupts are pending. ++ It is automatically cleared once read. */ ++ intstatus = emac->EMAC_ISR; ++ ++ if (intstatus & AT91C_EMAC_RCOM) /* Receive complete */ ++ at91ether_rx(dev); ++ ++ if (intstatus & AT91C_EMAC_TCOM) { /* Transmit complete */ ++ /* The TCOM bit is set even if the transmission failed. */ ++ if (intstatus & (AT91C_EMAC_TUND | AT91C_EMAC_RTRY)) ++ lp->stats.tx_errors += 1; ++ ++ dev_kfree_skb_irq(lp->skb); ++ pci_unmap_single(NULL, lp->skb_physaddr, lp->skb_length, PCI_DMA_TODEVICE); ++ netif_wake_queue(dev); ++ } ++ ++ if (intstatus & AT91C_EMAC_RBNA) ++ printk("%s: RBNA error\n", dev->name); ++ if (intstatus & AT91C_EMAC_ROVR) ++ printk("%s: ROVR error\n", dev->name); ++} ++ ++/* ++ * Initialize the ethernet interface ++ */ ++static int at91ether_setup(struct net_device *dev, unsigned long phy_type) ++{ ++ struct at91_private *lp; ++ AT91PS_EMAC regs; ++ static int already_initialized = 0; ++ unsigned int val; ++ ++ if (already_initialized) ++ return 0; ++ ++ dev = init_etherdev(dev, sizeof(struct at91_private)); ++ dev->base_addr = AT91C_VA_BASE_EMAC; ++ dev->irq = AT91C_ID_EMAC; ++ SET_MODULE_OWNER(dev); ++ ++ /* Install the interrupt handler */ ++ if (request_irq(dev->irq, at91ether_interrupt, 0, dev->name, dev)) ++ return -EBUSY; ++ ++ /* Allocate memory for private data structure */ ++ lp = (struct at91_private *) kmalloc(sizeof(struct at91_private), GFP_KERNEL); ++ if (lp == NULL) { ++ free_irq(dev->irq, dev); ++ return -ENOMEM; ++ } ++ memset(lp, 0, sizeof(struct at91_private)); ++ dev->priv = lp; ++ ++ /* Allocate memory for DMA Receive descriptors */ ++ lp->dlist = (struct recv_desc_bufs *) consistent_alloc(GFP_DMA | GFP_KERNEL, sizeof(struct recv_desc_bufs), (dma_addr_t *) &lp->dlist_phys); ++ if (lp->dlist == NULL) { ++ kfree(dev->priv); ++ free_irq(dev->irq, dev); ++ return -ENOMEM; ++ } ++ ++ spin_lock_init(&lp->lock); ++ ++ ether_setup(dev); ++ dev->open = at91ether_open; ++ dev->stop = at91ether_close; ++ dev->hard_start_xmit = at91ether_tx; ++ dev->get_stats = at91ether_stats; ++ dev->set_multicast_list = at91ether_set_rx_mode; ++ dev->do_ioctl = at91ether_ioctl; ++ ++#ifdef AT91_ETHER_ADDR_CONFIGURABLE ++ dev->set_mac_address = set_mac_address; ++#endif ++ ++ get_mac_address(dev); /* Get ethernet address and store it in dev->dev_addr */ ++ update_mac_address(dev); /* Program ethernet address into MAC */ ++ ++ regs = (AT91PS_EMAC) dev->base_addr; ++ regs->EMAC_CTL = 0; ++ ++#ifdef CONFIG_AT91_ETHER_RMII ++ regs->EMAC_CFG = AT91C_EMAC_BIG | AT91C_EMAC_RMII; ++#else ++ regs->EMAC_CFG = AT91C_EMAC_BIG; ++#endif ++ if (phy_type == MII_LXT971A_ID) ++ regs->EMAC_CFG |= AT91C_EMAC_CLK_HCLK_64; /* MDIO clock = system clock/64 */ ++ ++ if (phy_type == MII_DM9161_ID) { ++ spin_lock_irq(&lp->lock); ++ enable_mdi(regs); ++ ++ read_phy(regs, 0, MII_DSCR_REG, &val); ++ if ((val & (1 << 10)) == 0) /* DSCR bit 10 is 0 -- fiber mode */ ++ lp->phy_media = PORT_FIBRE; ++ ++ disable_mdi(regs); ++ spin_unlock_irq(&lp->lock); ++ } ++ ++ lp->mii.dev = dev; /* Support for ethtool */ ++ lp->mii.mdio_read = mdio_read; ++ lp->mii.mdio_write = mdio_write; ++ ++ lp->phy_type = phy_type; /* Type of PHY connected */ ++ ++ /* Determine current link speed */ ++ spin_lock_irq(&lp->lock); ++ enable_mdi(regs); ++ (void) update_linkspeed(dev, regs); ++ disable_mdi(regs); ++ spin_unlock_irq(&lp->lock); ++ ++ /* Display ethernet banner */ ++ printk(KERN_INFO "%s: AT91 ethernet at 0x%08x int=%d %s%s (%02x:%02x:%02x:%02x:%02x:%02x)\n", ++ dev->name, (uint) dev->base_addr, dev->irq, ++ regs->EMAC_CFG & AT91C_EMAC_SPD ? "100-" : "10-", ++ regs->EMAC_CFG & AT91C_EMAC_FD ? "FullDuplex" : "HalfDuplex", ++ dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], ++ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); ++ if (phy_type == MII_DM9161_ID) ++ printk(KERN_INFO "%s: Davicom 9196 PHY %s\n", dev->name, (lp->phy_media == PORT_FIBRE) ? "(Fiber)" : "(Copper)"); ++ else if (phy_type == MII_LXT971A_ID) ++ printk(KERN_INFO "%s: Intel LXT971A PHY\n", dev->name); ++ ++ already_initialized = 1; ++ return 0; ++} ++ ++/* ++ * Detect MAC and PHY and perform initialization ++ */ ++static int at91ether_probe(struct net_device *dev) ++{ ++ AT91PS_EMAC regs = (AT91PS_EMAC) AT91C_VA_BASE_EMAC; ++ unsigned int phyid1, phyid2; ++ int detected = -1; ++ ++ /* Configure the hardware - RMII vs MII mode */ ++#ifdef CONFIG_AT91_ETHER_RMII ++ AT91_CfgPIO_EMAC_RMII(); ++#else ++ AT91_CfgPIO_EMAC_MII(); ++#endif ++ ++ AT91_CfgPIO_EMAC_PHY(); /* Configure PHY interrupt */ ++ AT91_SYS->PMC_PCER = 1 << AT91C_ID_EMAC; /* Enable Peripheral clock */ ++ ++ /* Read the PHY ID registers */ ++ enable_mdi(regs); ++ read_phy(regs, 0, MII_PHYSID1, &phyid1); ++ read_phy(regs, 0, MII_PHYSID2, &phyid2); ++ disable_mdi(regs); ++ ++ /* Davicom 9161: PHY_ID1 = 0x181 PHY_ID2 = B881 */ ++ if (((phyid1 << 16) | (phyid2 & 0xfff0)) == MII_DM9161_ID) { ++ detected = at91ether_setup(dev, MII_DM9161_ID); ++ } ++ /* Intel LXT971A: PHY_ID1 = 0x13 PHY_ID2 = 78E0 */ ++ else if (((phyid1 << 16) | (phyid2 & 0xfff0)) == MII_LXT971A_ID) { ++ detected = at91ether_setup(dev, MII_LXT971A_ID); ++ } ++ ++ AT91_SYS->PMC_PCDR = 1 << AT91C_ID_EMAC; /* Disable Peripheral clock */ ++ ++ return detected; ++} ++ ++static int __init at91ether_init(void) ++{ ++ if (!at91ether_probe(&at91_dev)) ++ return register_netdev(&at91_dev); ++ ++ return -1; ++} ++ ++static void __exit at91ether_exit(void) ++{ ++ unregister_netdev(&at91_dev); ++} ++ ++module_init(at91ether_init) ++module_exit(at91ether_exit) ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("AT91RM9200 EMAC Ethernet driver"); ++MODULE_AUTHOR("Andrew Victor"); +diff -urN linux-2.4.26/drivers/at91/net/at91_ether.h linux-2.4.26-vrs1/drivers/at91/net/at91_ether.h +--- linux-2.4.26/drivers/at91/net/at91_ether.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/net/at91_ether.h 2004-03-04 22:07:29.000000000 +0000 +@@ -0,0 +1,81 @@ ++/* ++ * Ethernet driver for the Atmel AT91RM9200 (Thunder) ++ * ++ * (c) SAN People (Pty) Ltd ++ * ++ * Based on an earlier Atmel EMAC macrocell driver by Atmel and Lineo Inc. ++ * Initial version by Rick Bronson. ++ * ++ * 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. ++ */ ++ ++#ifndef AT91_ETHERNET ++#define AT91_ETHERNET ++ ++#undef AT91_ETHER_ADDR_CONFIGURABLE /* MAC address can be changed? */ ++ ++ ++/* Davicom 9161 PHY */ ++#define MII_DM9161_ID 0x0181b880 ++ ++/* Davicom specific registers */ ++#define MII_DSCR_REG 16 ++#define MII_DSCSR_REG 17 ++#define MII_DSINTR_REG 21 ++ ++/* Intel LXT971A PHY */ ++#define MII_LXT971A_ID 0x001378E0 ++ ++/* Intel specific registers */ ++#define MII_ISINTE_REG 18 ++#define MII_ISINTS_REG 19 ++ ++/* ........................................................................ */ ++ ++#define MAX_RBUFF_SZ 0x600 /* 1518 rounded up */ ++#define MAX_RX_DESCR 9 /* max number of receive buffers */ ++ ++#define EMAC_DESC_DONE 0x00000001 /* bit for if DMA is done */ ++#define EMAC_DESC_WRAP 0x00000002 /* bit for wrap */ ++ ++#define EMAC_BROADCAST 0x80000000 /* broadcast address */ ++#define EMAC_MULTICAST 0x40000000 /* multicast address */ ++#define EMAC_UNICAST 0x20000000 /* unicast address */ ++ ++struct rbf_t ++{ ++ unsigned int addr; ++ unsigned long size; ++}; ++ ++struct recv_desc_bufs ++{ ++ struct rbf_t descriptors[MAX_RX_DESCR]; /* must be on sizeof (rbf_t) boundary */ ++ char recv_buf[MAX_RX_DESCR][MAX_RBUFF_SZ]; /* must be on long boundary */ ++}; ++ ++struct at91_private ++{ ++ struct net_device_stats stats; ++ struct mii_if_info mii; /* ethtool support */ ++ ++ /* PHY */ ++ unsigned long phy_type; /* type of PHY (PHY_ID) */ ++ spinlock_t lock; /* lock for MDI interface */ ++ short phy_media; /* media interface type */ ++ ++ /* Transmit */ ++ struct sk_buff *skb; /* holds skb until xmit interrupt completes */ ++ dma_addr_t skb_physaddr; /* phys addr from pci_map_single */ ++ int skb_length; /* saved skb length for pci_unmap_single */ ++ ++ /* Receive */ ++ int rxBuffIndex; /* index into receive descriptor list */ ++ struct recv_desc_bufs *dlist; /* descriptor list address */ ++ struct recv_desc_bufs *dlist_phys; /* descriptor list physical address */ ++}; ++ ++#endif +diff -urN linux-2.4.26/drivers/at91/rtc/Makefile linux-2.4.26-vrs1/drivers/at91/rtc/Makefile +--- linux-2.4.26/drivers/at91/rtc/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/rtc/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,15 @@ ++# File: drivers/at91/rtc/Makefile ++# ++# Makefile for the Atmel AT91RM9200 real time clock device drivers ++# ++ ++O_TARGET := at91rtc.o ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++obj-$(CONFIG_AT91_RTC) += at91_rtc.o ++ ++include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/at91/rtc/at91_rtc.c linux-2.4.26-vrs1/drivers/at91/rtc/at91_rtc.c +--- linux-2.4.26/drivers/at91/rtc/at91_rtc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/rtc/at91_rtc.c 2004-03-04 22:31:24.000000000 +0000 +@@ -0,0 +1,441 @@ ++/* ++ * Real Time Clock interface for Linux on Atmel AT91RM9200 ++ * ++ * Copyright (c) 2002 Rick Bronson ++ * ++ * Based on sa1100-rtc.c by Nils Faerber ++ * Based on rtc.c by Paul Gortmaker ++ * Date/time conversion routines taken from arch/arm/kernel/time.c ++ * by Linus Torvalds and Russell King ++ * and the GNU C Library ++ * ( ... I love the GPL ... just take what you need! ;) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define AT91_RTC_FREQ 1 ++#define EPOCH 1970 ++ ++/* Those are the bits from a classic RTC we want to mimic */ ++#define AT91_RTC_IRQF 0x80 /* any of the following 3 is active */ ++#define AT91_RTC_PF 0x40 ++#define AT91_RTC_AF 0x20 ++#define AT91_RTC_UF 0x10 ++ ++#define BCD2BIN(val) (((val)&15) + ((val)>>4)*10) ++#define BIN2BCD(val) ((((val)/10)<<4) + (val)%10) ++ ++static unsigned long rtc_status = 0; ++static unsigned long rtc_irq_data; ++static unsigned int at91_alarm_year = EPOCH; ++ ++static struct fasync_struct *at91_rtc_async_queue; ++static DECLARE_WAIT_QUEUE_HEAD(at91_rtc_wait); ++static DECLARE_WAIT_QUEUE_HEAD(at91_rtc_update); ++static spinlock_t at91_rtc_updlock; /* some spinlocks for saving/restoring interrupt levels */ ++extern spinlock_t at91_rtc_lock; ++ ++static const unsigned char days_in_mo[] = ++ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; ++ ++#define is_leap(year) \ ++ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) ++ ++static const unsigned short int __mon_yday[2][13] = ++{ ++ /* Normal years. */ ++ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, ++ /* Leap years. */ ++ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } ++}; ++ ++/* ++ * Returns day since start of the year [0-365] ++ * (from drivers/char/efirtc.c) ++ */ ++static inline int compute_yday(int year, int month, int day) ++{ ++ return __mon_yday[is_leap(year)][month] + day-1; ++} ++ ++/* ++ * Set current time and date in RTC ++ */ ++static void at91_rtc_settime(struct rtc_time *tval) ++{ ++ unsigned long flags; ++ ++ /* Stop Time/Calendar from counting */ ++ AT91_SYS->RTC_CR |= (AT91C_RTC_UPDCAL | AT91C_RTC_UPDTIM); ++ ++ spin_lock_irqsave(&at91_rtc_updlock, flags); /* stop int's else we wakeup b4 we sleep */ ++ AT91_SYS->RTC_IER = AT91C_RTC_ACKUPD; ++ interruptible_sleep_on(&at91_rtc_update); /* wait for ACKUPD interrupt to hit */ ++ spin_unlock_irqrestore(&at91_rtc_updlock, flags); ++ AT91_SYS->RTC_IDR = AT91C_RTC_ACKUPD; ++ ++ AT91_SYS->RTC_TIMR = BIN2BCD(tval->tm_sec) << 0 ++ | BIN2BCD(tval->tm_min) << 8 ++ | BIN2BCD(tval->tm_hour) << 16; ++ ++ AT91_SYS->RTC_CALR = BIN2BCD((tval->tm_year + 1900) / 100) /* century */ ++ | BIN2BCD(tval->tm_year % 100) << 8 /* year */ ++ | BIN2BCD(tval->tm_mon + 1) << 16 /* tm_mon starts at zero */ ++ | BIN2BCD(tval->tm_wday + 1) << 21 /* day of the week [0-6], Sunday=0 */ ++ | BIN2BCD(tval->tm_mday) << 24; ++ ++ /* Restart Time/Calendar */ ++ AT91_SYS->RTC_CR &= ~(AT91C_RTC_UPDCAL | AT91C_RTC_UPDTIM); ++} ++ ++/* ++ * Decode time/date into rtc_time structure ++ */ ++static void at91_rtc_decodetime(AT91_REG *timereg, AT91_REG *calreg, struct rtc_time *tval) ++{ ++ unsigned int time, date; ++ ++ do { /* must read twice in case it changes */ ++ time = *timereg; ++ date = *calreg; ++ } while ((time != *timereg) || (date != *calreg)); ++ ++ tval->tm_sec = BCD2BIN((time & AT91C_RTC_SEC) >> 0); ++ tval->tm_min = BCD2BIN((time & AT91C_RTC_MIN) >> 8); ++ tval->tm_hour = BCD2BIN((time & AT91C_RTC_HOUR) >> 16); ++ ++ /* The Calendar Alarm register does not have a field for ++ the year - so these will return an invalid value. When an ++ alarm is set, at91_alarm_year wille store the current year. */ ++ tval->tm_year = BCD2BIN(date & AT91C_RTC_CENT) * 100; /* century */ ++ tval->tm_year += BCD2BIN((date & AT91C_RTC_YEAR) >> 8); /* year */ ++ ++ tval->tm_wday = BCD2BIN((date & AT91C_RTC_DAY) >> 21) - 1; /* day of the week [0-6], Sunday=0 */ ++ tval->tm_mon = BCD2BIN(((date & AT91C_RTC_MONTH) >> 16) - 1); ++ tval->tm_mday = BCD2BIN((date & AT91C_RTC_DATE) >> 24); ++} ++ ++/* ++ * IRQ handler for the RTC ++ */ ++static void at91_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ unsigned int rtsr = AT91_SYS->RTC_SR & AT91_SYS->RTC_IMR; ++ ++ /* update irq data & counter */ ++ if (rtsr) { /* this interrupt is shared! Is it ours? */ ++ if (rtsr & AT91C_RTC_ALARM) ++ rtc_irq_data |= (AT91_RTC_AF | AT91_RTC_IRQF); ++ if (rtsr & AT91C_RTC_SECEV) ++ rtc_irq_data |= (AT91_RTC_UF | AT91_RTC_IRQF); ++ if (rtsr & AT91C_RTC_ACKUPD) ++ wake_up_interruptible(&at91_rtc_update); ++ rtc_irq_data += 0x100; ++ AT91_SYS->RTC_SCCR = rtsr; /* clear status reg */ ++ ++ /* wake up waiting process */ ++ wake_up_interruptible(&at91_rtc_wait); ++ kill_fasync(&at91_rtc_async_queue, SIGIO, POLL_IN); ++ } ++} ++ ++static int at91_rtc_open(struct inode *inode, struct file *file) ++{ ++ if (test_and_set_bit(1, &rtc_status)) ++ return -EBUSY; ++ rtc_irq_data = 0; ++ return 0; ++} ++ ++static int at91_rtc_release(struct inode *inode, struct file *file) ++{ ++ rtc_status = 0; ++ return 0; ++} ++ ++static int at91_rtc_fasync(int fd, struct file *filp, int on) ++{ ++ return fasync_helper(fd, filp, on, &at91_rtc_async_queue); ++} ++ ++static unsigned int at91_rtc_poll(struct file *file, poll_table * wait) ++{ ++ poll_wait(file, &at91_rtc_wait, wait); ++ return (rtc_irq_data) ? 0 : POLLIN | POLLRDNORM; ++} ++ ++static ssize_t at91_rtc_read(struct file * file, char *buf, size_t count, loff_t * ppos) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long data; ++ ssize_t retval; ++ ++ if (count < sizeof(unsigned long)) ++ return -EINVAL; ++ ++ add_wait_queue(&at91_rtc_wait, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ for (;;) { ++ spin_lock_irq(&at91_rtc_lock); ++ data = rtc_irq_data; ++ if (data != 0) { ++ rtc_irq_data = 0; ++ break; ++ } ++ spin_unlock_irq(&at91_rtc_lock); ++ ++ if (file->f_flags & O_NONBLOCK) { ++ retval = -EAGAIN; ++ goto out; ++ } ++ ++ if (signal_pending(current)) { ++ retval = -ERESTARTSYS; ++ goto out; ++ } ++ ++ schedule(); ++ } ++ spin_unlock_irq(&at91_rtc_lock); ++ ++ data -= 0x100; /* the first IRQ wasn't actually missed */ ++ retval = put_user(data, (unsigned long *) buf); ++ if (!retval) ++ retval = sizeof(unsigned long); ++ ++out: ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&at91_rtc_wait, &wait); ++ return retval; ++} ++ ++/* ++ * Handle commands from user-space ++ */ ++static int at91_rtc_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct rtc_time tm, tm2; ++ int ret = 0; ++ ++ spin_lock_irq(&at91_rtc_lock); ++ switch (cmd) { ++ case RTC_AIE_OFF: /* alarm off */ ++ AT91_SYS->RTC_IDR = AT91C_RTC_ALARM; ++ rtc_irq_data = 0; ++ break; ++ case RTC_AIE_ON: /* alarm on */ ++ AT91_SYS->RTC_IER = AT91C_RTC_ALARM; ++ rtc_irq_data = 0; ++ break; ++ case RTC_UIE_OFF: /* update off */ ++ AT91_SYS->RTC_IDR = AT91C_RTC_SECEV; ++ rtc_irq_data = 0; ++ break; ++ case RTC_UIE_ON: /* update on */ ++ AT91_SYS->RTC_IER = AT91C_RTC_SECEV; ++ rtc_irq_data = 0; ++ break; ++ case RTC_PIE_OFF: /* periodic off */ ++ AT91_SYS->RTC_IDR = AT91C_RTC_SECEV; ++ rtc_irq_data = 0; ++ break; ++ case RTC_PIE_ON: /* periodic on */ ++ AT91_SYS->RTC_IER = AT91C_RTC_SECEV; ++ rtc_irq_data = 0; ++ break; ++ case RTC_ALM_READ: /* read alarm */ ++ memset(&tm, 0, sizeof(struct rtc_time)); ++ at91_rtc_decodetime(&(AT91_SYS->RTC_TIMALR), &(AT91_SYS->RTC_CALALR), &tm); ++ tm.tm_yday = compute_yday(tm.tm_year, tm.tm_mon, tm.tm_mday); ++ tm.tm_year = at91_alarm_year - 1900; ++ ret = copy_to_user((void *) arg, &tm, sizeof(tm)) ? -EFAULT : 0; ++ break; ++ case RTC_ALM_SET: /* set alarm */ ++ if (copy_from_user(&tm2, (struct rtc_time *) arg, sizeof(tm2))) ++ ret = -EFAULT; ++ else { ++ at91_rtc_decodetime(&(AT91_SYS->RTC_TIMR), &(AT91_SYS->RTC_CALR), &tm); ++ at91_alarm_year = tm.tm_year; ++ if ((unsigned) tm2.tm_hour < 24) /* do some range checking */ ++ tm.tm_hour = tm2.tm_hour; ++ if ((unsigned) tm2.tm_min < 60) ++ tm.tm_min = tm2.tm_min; ++ if ((unsigned) tm2.tm_sec < 60) ++ tm.tm_sec = tm2.tm_sec; ++ AT91_SYS->RTC_TIMALR = BIN2BCD(tm.tm_sec) << 0 ++ | BIN2BCD(tm.tm_min) << 8 ++ | BIN2BCD(tm.tm_hour) << 16 ++ | AT91C_RTC_HOUREN | AT91C_RTC_MINEN ++ | AT91C_RTC_SECEN; ++ AT91_SYS->RTC_CALALR = BIN2BCD(tm.tm_mon + 1) << 16 /* tm_mon starts at zero */ ++ | BIN2BCD(tm.tm_mday) << 24 ++ | AT91C_RTC_DATEEN | AT91C_RTC_MONTHEN; ++ } ++ break; ++ case RTC_RD_TIME: /* read time */ ++ memset(&tm, 0, sizeof(struct rtc_time)); ++ at91_rtc_decodetime(&(AT91_SYS->RTC_TIMR), &(AT91_SYS->RTC_CALR), &tm); ++ tm.tm_yday = compute_yday(tm.tm_year, tm.tm_mon, tm.tm_mday); ++ tm.tm_year = tm.tm_year - 1900; ++ ret = copy_to_user((void *) arg, &tm, sizeof(tm)) ? -EFAULT : 0; ++ break; ++ case RTC_SET_TIME: /* set time */ ++ if (!capable(CAP_SYS_TIME)) ++ ret = -EACCES; ++ else { ++ if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(tm))) ++ ret = -EFAULT; ++ else { ++ int tm_year = tm.tm_year + 1900; ++ if (tm_year < EPOCH ++ || (unsigned) tm.tm_mon >= 12 ++ || tm.tm_mday < 1 ++ || tm.tm_mday > (days_in_mo[tm.tm_mon] + (tm.tm_mon == 1 && is_leap(tm_year))) ++ || (unsigned) tm.tm_hour >= 24 ++ || (unsigned) tm.tm_min >= 60 ++ || (unsigned) tm.tm_sec >= 60) ++ ret = -EINVAL; ++ else ++ at91_rtc_settime(&tm); ++ } ++ } ++ break; ++ case RTC_IRQP_READ: /* read periodic alarm frequency */ ++ ret = put_user(AT91_RTC_FREQ, (unsigned long *) arg); ++ break; ++ case RTC_IRQP_SET: /* set periodic alarm frequency */ ++ if (arg != AT91_RTC_FREQ) ++ ret = -EINVAL; ++ break; ++ case RTC_EPOCH_READ: /* read epoch */ ++ ret = put_user(EPOCH, (unsigned long *) arg); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ spin_unlock_irq(&at91_rtc_lock); ++ return ret; ++} ++ ++/* ++ * Provide RTC information in /proc/driver/rtc ++ */ ++static int at91_rtc_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ char *p = page; ++ int len; ++ struct rtc_time tm; ++ ++ at91_rtc_decodetime(&(AT91_SYS->RTC_TIMR), &(AT91_SYS->RTC_CALR), &tm); ++ p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" ++ "rtc_date\t: %04d-%02d-%02d\n" ++ "rtc_epoch\t: %04d\n", ++ tm.tm_hour, tm.tm_min, tm.tm_sec, ++ tm.tm_year, tm.tm_mon + 1, tm.tm_mday, EPOCH); ++ at91_rtc_decodetime(&(AT91_SYS->RTC_TIMALR), &(AT91_SYS->RTC_CALALR), &tm); ++ p += sprintf(p, "alrm_time\t: %02d:%02d:%02d\n" ++ "alrm_date\t: %04d-%02d-%02d\n", ++ tm.tm_hour, tm.tm_min, tm.tm_sec, ++ at91_alarm_year, tm.tm_mon + 1, tm.tm_mday); ++ p += sprintf(p, "alarm_IRQ\t: %s\n", (AT91_SYS->RTC_IMR & AT91C_RTC_ALARM) ? "yes" : "no"); ++ p += sprintf(p, "update_IRQ\t: %s\n", (AT91_SYS->RTC_IMR & AT91C_RTC_ACKUPD) ? "yes" : "no"); ++ p += sprintf(p, "periodic_IRQ\t: %s\n", (AT91_SYS->RTC_IMR & AT91C_RTC_SECEV) ? "yes" : "no"); ++ p += sprintf(p, "periodic_freq\t: %ld\n", (unsigned long) AT91_RTC_FREQ); ++ ++ len = (p - page) - off; ++ if (len < 0) ++ len = 0; ++ ++ *eof = (len <= count) ? 1 : 0; ++ *start = page + off; ++ ++ return len; ++} ++ ++static struct file_operations at91_rtc_fops = { ++ owner:THIS_MODULE, ++ llseek:no_llseek, ++ read:at91_rtc_read, ++ poll:at91_rtc_poll, ++ ioctl:at91_rtc_ioctl, ++ open:at91_rtc_open, ++ release:at91_rtc_release, ++ fasync:at91_rtc_fasync, ++}; ++ ++static struct miscdevice at91_rtc_miscdev = { ++ minor:RTC_MINOR, ++ name:"rtc", ++ fops:&at91_rtc_fops, ++}; ++ ++/* ++ * Initialize and install RTC driver ++ */ ++static int __init at91_rtc_init(void) ++{ ++ int ret; ++ ++ AT91_SYS->RTC_CR = 0; ++ AT91_SYS->RTC_MR = 0; /* put in 24 hour format */ ++ /* Disable all interrupts */ ++ AT91_SYS->RTC_IDR = AT91C_RTC_ACKUPD | AT91C_RTC_ALARM | AT91C_RTC_SECEV | AT91C_RTC_TIMEV | AT91C_RTC_CALEV; ++ ++ spin_lock_init(&at91_rtc_updlock); ++ spin_lock_init(&at91_rtc_lock); ++ ++ misc_register(&at91_rtc_miscdev); ++ create_proc_read_entry("driver/rtc", 0, 0, at91_rtc_read_proc, NULL); ++ ret = request_irq(AT91C_ID_SYS, at91_rtc_interrupt, SA_SHIRQ, ++ "at91_rtc", &rtc_status); ++ if (ret) { ++ printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n", AT91C_ID_SYS); ++ remove_proc_entry("driver/rtc", NULL); ++ misc_deregister(&at91_rtc_miscdev); ++ return ret; ++ } ++ ++ printk(KERN_INFO "AT91 Real Time Clock driver\n"); ++ return 0; ++} ++ ++/* ++ * Disable and remove the RTC driver ++ */ ++static void __exit at91_rtc_exit(void) ++{ ++ /* Disable all interrupts */ ++ AT91_SYS->RTC_IDR = AT91C_RTC_ACKUPD | AT91C_RTC_ALARM | AT91C_RTC_SECEV | AT91C_RTC_TIMEV | AT91C_RTC_CALEV; ++ free_irq(AT91C_ID_SYS, &rtc_status); ++ ++ rtc_status = 0; ++ remove_proc_entry("driver/rtc", NULL); ++ misc_deregister(&at91_rtc_miscdev); ++} ++ ++module_init(at91_rtc_init); ++module_exit(at91_rtc_exit); ++ ++MODULE_AUTHOR("Rick Bronson"); ++MODULE_DESCRIPTION("AT91 Realtime Clock Driver (AT91_RTC)"); ++MODULE_LICENSE("GPL"); ++EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/at91/serial/Makefile linux-2.4.26-vrs1/drivers/at91/serial/Makefile +--- linux-2.4.26/drivers/at91/serial/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/serial/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,15 @@ ++# File: drivers/at91/serial/Makefile ++# ++# Makefile for the Atmel AT91RM9200 serial and console device drivers ++# ++ ++O_TARGET := at91serial.o ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++obj-$(CONFIG_SERIAL_AT91) += at91_serial.o ++ ++include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/at91/serial/at91_serial.c linux-2.4.26-vrs1/drivers/at91/serial/at91_serial.c +--- linux-2.4.26/drivers/at91/serial/at91_serial.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/serial/at91_serial.c 2004-04-09 15:12:35.000000000 +0100 +@@ -0,0 +1,870 @@ ++/* ++ * linux/drivers/char/at91_serial.c ++ * ++ * Driver for Atmel AT91RM9200 Serial ports ++ * ++ * Copyright (c) Rick Bronson ++ * ++ * Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd. ++ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++ ++#if defined(CONFIG_SERIAL_AT91_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ++#define SUPPORT_SYSRQ ++#endif ++ ++#include ++ ++#define SERIAL_AT91_MAJOR TTY_MAJOR ++#define CALLOUT_AT91_MAJOR TTYAUX_MAJOR ++#define MINOR_START 64 ++ ++#define AT91C_VA_BASE_DBGU ((unsigned long) &(AT91_SYS->DBGU_CR)) ++#define AT91_ISR_PASS_LIMIT 256 ++ ++#define UART_PUT_CR(port,v) ((AT91PS_USART)(port)->membase)->US_CR = v ++#define UART_GET_MR(port) ((AT91PS_USART)(port)->membase)->US_MR ++#define UART_PUT_MR(port,v) ((AT91PS_USART)(port)->membase)->US_MR = v ++#define UART_PUT_IER(port,v) ((AT91PS_USART)(port)->membase)->US_IER = v ++#define UART_PUT_IDR(port,v) ((AT91PS_USART)(port)->membase)->US_IDR = v ++#define UART_GET_IMR(port) ((AT91PS_USART)(port)->membase)->US_IMR ++#define UART_GET_CSR(port) ((AT91PS_USART)(port)->membase)->US_CSR ++#define UART_GET_CHAR(port) ((AT91PS_USART)(port)->membase)->US_RHR ++#define UART_PUT_CHAR(port,v) ((AT91PS_USART)(port)->membase)->US_THR = v ++#define UART_GET_BRGR(port) ((AT91PS_USART)(port)->membase)->US_BRGR ++#define UART_PUT_BRGR(port,v) ((AT91PS_USART)(port)->membase)->US_BRGR = v ++#define UART_PUT_RTOR(port,v) ((AT91PS_USART)(port)->membase)->US_RTOR = v ++ ++// #define UART_GET_CR(port) ((AT91PS_USART)(port)->membase)->US_CR // is write-only ++ ++ /* PDC registers */ ++#define UART_PUT_PTCR(port,v) ((AT91PS_USART)(port)->membase)->US_PTCR = v ++#define UART_PUT_RPR(port,v) ((AT91PS_USART)(port)->membase)->US_RPR = v ++#define UART_PUT_RCR(port,v) ((AT91PS_USART)(port)->membase)->US_RCR = v ++#define UART_GET_RCR(port) ((AT91PS_USART)(port)->membase)->US_RCR ++#define UART_PUT_RNPR(port,v) ((AT91PS_USART)(port)->membase)->US_RNPR = v ++#define UART_PUT_RNCR(port,v) ((AT91PS_USART)(port)->membase)->US_RNCR = v ++ ++static struct tty_driver normal, callout; ++static struct tty_struct *at91_table[AT91C_NR_UART]; ++static struct termios *at91_termios[AT91C_NR_UART], *at91_termios_locked[AT91C_NR_UART]; ++ ++const int at91_serialmap[AT91C_NR_UART] = AT91C_UART_MAP; ++ ++static int (*at91_open)(struct uart_port *); ++static void (*at91_close)(struct uart_port *); ++ ++#ifdef SUPPORT_SYSRQ ++static struct console at91_console; ++#endif ++ ++/* ++ * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. ++ */ ++static u_int at91_tx_empty(struct uart_port *port) ++{ ++ return UART_GET_CSR(port) & AT91C_US_TXEMPTY ? TIOCSER_TEMT : 0; ++} ++ ++/* ++ * Set state of the modem control output lines ++ */ ++static void at91_set_mctrl(struct uart_port *port, u_int mctrl) ++{ ++ unsigned int control = 0; ++ ++ if (mctrl & TIOCM_RTS) ++ control |= AT91C_US_RTSEN; ++ else ++ control |= AT91C_US_RTSDIS; ++ ++ if (mctrl & TIOCM_DTR) ++ control |= AT91C_US_DTREN; ++ else ++ control |= AT91C_US_DTRDIS; ++ ++ UART_PUT_CR(port,control); ++} ++ ++/* ++ * Get state of the modem control input lines ++ */ ++static u_int at91_get_mctrl(struct uart_port *port) ++{ ++ unsigned int status, ret = 0; ++ ++ status = UART_GET_CSR(port); ++ if (status & AT91C_US_DCD) ++ ret |= TIOCM_CD; ++ if (status & AT91C_US_CTS) ++ ret |= TIOCM_CTS; ++ if (status & AT91C_US_DSR) ++ ret |= TIOCM_DSR; ++ if (status & AT91C_US_RI) ++ ret |= TIOCM_RI; ++ ++ return ret; ++} ++ ++/* ++ * Stop transmitting. ++ */ ++static void at91_stop_tx(struct uart_port *port, u_int from_tty) ++{ ++ UART_PUT_IDR(port, AT91C_US_TXRDY); ++ port->read_status_mask &= ~AT91C_US_TXRDY; ++} ++ ++/* ++ * Start transmitting. ++ */ ++static void at91_start_tx(struct uart_port *port, u_int from_tty) ++{ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ port->read_status_mask |= AT91C_US_TXRDY; ++ UART_PUT_IER(port, AT91C_US_TXRDY); ++ local_irq_restore(flags); ++} ++ ++/* ++ * Stop receiving - port is in process of being closed. ++ */ ++static void at91_stop_rx(struct uart_port *port) ++{ ++ UART_PUT_IDR(port, AT91C_US_RXRDY); ++} ++ ++/* ++ * Enable modem status interrupts ++ */ ++static void at91_enable_ms(struct uart_port *port) ++{ ++ UART_PUT_IER(port, AT91C_US_RIIC | AT91C_US_DSRIC | AT91C_US_DCDIC | AT91C_US_CTSIC); ++} ++ ++/* ++ * Control the transmission of a break signal ++ */ ++static void at91_break_ctl(struct uart_port *port, int break_state) ++{ ++ if (break_state != 0) ++ UART_PUT_CR(port, AT91C_US_STTBRK); /* start break */ ++ else ++ UART_PUT_CR(port, AT91C_US_STPBRK); /* stop break */ ++} ++ ++/* ++ * Characters received (called from interrupt handler) ++ */ ++static void at91_rx_chars(struct uart_port *port, struct pt_regs *regs) ++{ ++ struct uart_info *info = port->info; ++ struct tty_struct *tty = info->tty; ++ unsigned int status, ch, flg, ignored = 0; ++ ++ status = UART_GET_CSR(port); ++ while (status & (AT91C_US_RXRDY)) { ++ ch = UART_GET_CHAR(port); ++ ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) ++ goto ignore_char; ++ port->icount.rx++; ++ ++ flg = TTY_NORMAL; ++ ++ /* ++ * note that the error handling code is ++ * out of the main execution path ++ */ ++ if (status & (AT91C_US_PARE | AT91C_US_FRAME | AT91C_US_OVRE)) ++ goto handle_error; ++ ++ if (uart_handle_sysrq_char(port, ch, regs)) ++ goto ignore_char; ++ ++ error_return: ++ *tty->flip.flag_buf_ptr++ = flg; ++ *tty->flip.char_buf_ptr++ = ch; ++ tty->flip.count++; ++ ignore_char: ++ status = UART_GET_CSR(port); ++ } ++out: ++ tty_flip_buffer_push(tty); ++ return; ++ ++handle_error: ++ if (status & (AT91C_US_PARE | AT91C_US_FRAME | AT91C_US_OVRE)) ++ UART_PUT_CR(port, AT91C_US_RSTSTA); /* clear error */ ++ if (status & (AT91C_US_PARE)) ++ port->icount.parity++; ++ else if (status & (AT91C_US_FRAME)) ++ port->icount.frame++; ++ if (status & (AT91C_US_OVRE)) ++ port->icount.overrun++; ++ ++ if (status & port->ignore_status_mask) { ++ if (++ignored > 100) ++ goto out; ++ goto ignore_char; ++ } ++ ++ status &= port->read_status_mask; ++ ++ UART_PUT_CR(port, AT91C_US_RSTSTA); /* clear error */ ++ if (status & AT91C_US_PARE) ++ flg = TTY_PARITY; ++ else if (status & AT91C_US_FRAME) ++ flg = TTY_FRAME; ++ ++ if (status & AT91C_US_OVRE) { ++ /* ++ * overrun does *not* affect the character ++ * we read from the FIFO ++ */ ++ *tty->flip.flag_buf_ptr++ = flg; ++ *tty->flip.char_buf_ptr++ = ch; ++ tty->flip.count++; ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) ++ goto ignore_char; ++ ch = 0; ++ flg = TTY_OVERRUN; ++ } ++#ifdef SUPPORT_SYSRQ ++ port->sysrq = 0; ++#endif ++ goto error_return; ++} ++ ++/* ++ * Transmit characters (called from interrupt handler) ++ */ ++static void at91_tx_chars(struct uart_port *port) ++{ ++ struct circ_buf *xmit = &port->info->xmit; ++ ++ if (port->x_char) { ++ UART_PUT_CHAR(port, port->x_char); ++ port->icount.tx++; ++ port->x_char = 0; ++ return; ++ } ++ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { ++ at91_stop_tx(port, 0); ++ return; ++ } ++ ++ while (UART_GET_CSR(port) & AT91C_US_TXRDY) { ++ UART_PUT_CHAR(port, xmit->buf[xmit->tail]); ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ port->icount.tx++; ++ if (uart_circ_empty(xmit)) ++ break; ++ } ++ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(port); ++ ++ if (uart_circ_empty(xmit)) ++ at91_stop_tx(port, 0); ++} ++ ++/* ++ * Interrupt handler ++ */ ++static void at91_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct uart_port *port = dev_id; ++ unsigned int status, pending, pass_counter = 0; ++ ++ status = UART_GET_CSR(port); ++ pending = status & port->read_status_mask; ++ if (pending) { ++ do { ++ if (pending & AT91C_US_RXRDY) ++ at91_rx_chars(port, regs); ++ ++ /* Clear the relevent break bits */ ++ if (pending & AT91C_US_RXBRK) { ++ UART_PUT_CR(port, AT91C_US_RSTSTA); ++ port->icount.brk++; ++#ifdef SUPPORT_SYSRQ ++ if (port->line == at91_console.index && !port->sysrq) { ++ port->sysrq = jiffies + HZ*5; ++ } ++#endif ++ } ++ ++ // TODO: All reads to CSR will clear these interrupts! ++ if (pending & AT91C_US_RIIC) port->icount.rng++; ++ if (pending & AT91C_US_DSRIC) port->icount.dsr++; ++ if (pending & AT91C_US_DCDIC) { ++ port->icount.dcd++; ++ uart_handle_dcd_change(port, status & AT91C_US_DCD); ++ } ++ if (pending & AT91C_US_CTSIC) { ++ port->icount.cts++; ++ uart_handle_cts_change(port, status & AT91C_US_CTS); ++ } ++ if (pending & (AT91C_US_RIIC | AT91C_US_DSRIC | AT91C_US_DCDIC | AT91C_US_CTSIC)) ++ wake_up_interruptible(&port->info->delta_msr_wait); ++ ++ if (pending & AT91C_US_TXRDY) ++ at91_tx_chars(port); ++ if (pass_counter++ > AT91_ISR_PASS_LIMIT) ++ break; ++ ++ status = UART_GET_CSR(port); ++ pending = status & port->read_status_mask; ++ } while (pending); ++ } ++} ++ ++/* ++ * Perform initialization and enable port for reception ++ */ ++static int at91_startup(struct uart_port *port) ++{ ++ int retval; ++ ++ /* ++ * Allocate the IRQ ++ */ ++ retval = request_irq(port->irq, at91_interrupt, SA_SHIRQ, "at91_serial", port); ++ if (retval) { ++ printk("at91_serial: at91_startup - Can't get irq\n"); ++ return retval; ++ } ++ /* ++ * If there is a specific "open" function (to register ++ * control line interrupts) ++ */ ++ if (at91_open) { ++ retval = at91_open(port); ++ if (retval) { ++ free_irq(port->irq, port); ++ return retval; ++ } ++ } ++ ++ /* Enable peripheral clock if required */ ++ if (port->irq != AT91C_ID_SYS) ++ AT91_SYS->PMC_PCER = 1 << port->irq; ++ ++ port->read_status_mask = AT91C_US_RXRDY | AT91C_US_TXRDY | AT91C_US_OVRE ++ | AT91C_US_FRAME | AT91C_US_PARE | AT91C_US_RXBRK; ++ /* ++ * Finally, clear and enable interrupts ++ */ ++ UART_PUT_IDR(port, -1); ++ UART_PUT_CR(port, AT91C_US_TXEN | AT91C_US_RXEN); /* enable xmit & rcvr */ ++ UART_PUT_IER(port, AT91C_US_RXRDY); /* do receive only */ ++ return 0; ++} ++ ++/* ++ * Disable the port ++ */ ++static void at91_shutdown(struct uart_port *port) ++{ ++ /* ++ * Free the interrupt ++ */ ++ free_irq(port->irq, port); ++ ++ /* ++ * If there is a specific "close" function (to unregister ++ * control line interrupts) ++ */ ++ if (at91_close) ++ at91_close(port); ++ ++ /* ++ * Disable all interrupts, port and break condition. ++ */ ++ UART_PUT_CR(port, AT91C_US_RSTSTA); ++ UART_PUT_IDR(port, -1); ++ ++ /* Disable peripheral clock if required */ ++ if (port->irq != AT91C_ID_SYS) ++ AT91_SYS->PMC_PCDR = 1 << port->irq; ++} ++ ++static struct uart_ops at91_pops; /* forward declaration */ ++ ++/* ++ * Change the port parameters ++ */ ++static void at91_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot) ++{ ++ unsigned long flags; ++ unsigned int mode, imr; ++ ++ /* Get current mode register */ ++ mode = UART_GET_MR(port) & ~(AT91C_US_CHRL | AT91C_US_NBSTOP | AT91C_US_PAR); ++ ++ /* byte size */ ++ switch (cflag & CSIZE) { ++ case CS5: ++ mode |= AT91C_US_CHRL_5_BITS; ++ break; ++ case CS6: ++ mode |= AT91C_US_CHRL_6_BITS; ++ break; ++ case CS7: ++ mode |= AT91C_US_CHRL_7_BITS; ++ break; ++ default: ++ mode |= AT91C_US_CHRL_8_BITS; ++ break; ++ } ++ ++ /* stop bits */ ++ if (cflag & CSTOPB) ++ mode |= AT91C_US_NBSTOP_2_BIT; ++ ++ /* parity */ ++ if (cflag & PARENB) { ++ if (cflag & CMSPAR) { /* Mark or Space parity */ ++ if (cflag & PARODD) ++ mode |= AT91C_US_PAR_MARK; ++ else ++ mode |= AT91C_US_PAR_SPACE; ++ } ++ else if (cflag & PARODD) ++ mode |= AT91C_US_PAR_ODD; ++ else ++ mode |= AT91C_US_PAR_EVEN; ++ } ++ else ++ mode |= AT91C_US_PAR_NONE; ++ ++ port->read_status_mask |= AT91C_US_OVRE; ++ if (iflag & INPCK) ++ port->read_status_mask |= AT91C_US_FRAME | AT91C_US_PARE; ++ if (iflag & (BRKINT | PARMRK)) ++ port->read_status_mask |= AT91C_US_RXBRK; ++ ++ /* ++ * Characters to ignore ++ */ ++ port->ignore_status_mask = 0; ++ if (iflag & IGNPAR) ++ port->ignore_status_mask |= (AT91C_US_FRAME | AT91C_US_PARE); ++ if (iflag & IGNBRK) { ++ port->ignore_status_mask |= AT91C_US_RXBRK; ++ /* ++ * If we're ignoring parity and break indicators, ++ * ignore overruns too (for real raw support). ++ */ ++ if (iflag & IGNPAR) ++ port->ignore_status_mask |= AT91C_US_OVRE; ++ } ++ ++ // TODO: Ignore all characters if CREAD is set. ++ ++ /* first, disable interrupts and drain transmitter */ ++ local_irq_save(flags); ++ imr = UART_GET_IMR(port); /* get interrupt mask */ ++ UART_PUT_IDR(port, -1); /* disable all interrupts */ ++ local_irq_restore(flags); ++ while (!(UART_GET_CSR(port) & AT91C_US_TXEMPTY)) { barrier(); } ++ ++ /* disable receiver and transmitter */ ++ UART_PUT_CR(port, AT91C_US_TXDIS | AT91C_US_RXDIS); ++ ++ /* set the parity, stop bits and data size */ ++ UART_PUT_MR(port, mode); ++ ++ /* set the baud rate */ ++ UART_PUT_BRGR(port, quot); ++ UART_PUT_CR(port, AT91C_US_TXEN | AT91C_US_RXEN); ++ ++ /* restore interrupts */ ++ UART_PUT_IER(port, imr); ++ ++ /* CTS flow-control and modem-status interrupts */ ++ if (UART_ENABLE_MS(port, cflag)) ++ at91_pops.enable_ms(port); ++} ++ ++/* ++ * Return string describing the specified port ++ */ ++static const char *at91_type(struct uart_port *port) ++{ ++ return port->type == PORT_AT91RM9200 ? "AT91_SERIAL" : NULL; ++} ++ ++/* ++ * Release the memory region(s) being used by 'port'. ++ */ ++static void at91_release_port(struct uart_port *port) ++{ ++ release_mem_region(port->mapbase, ++ port->mapbase == AT91C_VA_BASE_DBGU ? 512 : SZ_16K); ++} ++ ++/* ++ * Request the memory region(s) being used by 'port'. ++ */ ++static int at91_request_port(struct uart_port *port) ++{ ++ return request_mem_region(port->mapbase, ++ port->mapbase == AT91C_VA_BASE_DBGU ? 512 : SZ_16K, ++ "at91_serial") != NULL ? 0 : -EBUSY; ++ ++} ++ ++/* ++ * Configure/autoconfigure the port. ++ */ ++static void at91_config_port(struct uart_port *port, int flags) ++{ ++ if (flags & UART_CONFIG_TYPE) { ++ port->type = PORT_AT91RM9200; ++ at91_request_port(port); ++ } ++} ++ ++/* ++ * Verify the new serial_struct (for TIOCSSERIAL). ++ */ ++static int at91_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ int ret = 0; ++ if (ser->type != PORT_UNKNOWN && ser->type != PORT_AT91RM9200) ++ ret = -EINVAL; ++ if (port->irq != ser->irq) ++ ret = -EINVAL; ++ if (ser->io_type != SERIAL_IO_MEM) ++ ret = -EINVAL; ++ if (port->uartclk / 16 != ser->baud_base) ++ ret = -EINVAL; ++ if ((void *)port->mapbase != ser->iomem_base) ++ ret = -EINVAL; ++ if (port->iobase != ser->port) ++ ret = -EINVAL; ++ if (ser->hub6 != 0) ++ ret = -EINVAL; ++ return ret; ++} ++ ++static struct uart_ops at91_pops = { ++ tx_empty: at91_tx_empty, ++ set_mctrl: at91_set_mctrl, ++ get_mctrl: at91_get_mctrl, ++ stop_tx: at91_stop_tx, ++ start_tx: at91_start_tx, ++ stop_rx: at91_stop_rx, ++ enable_ms: at91_enable_ms, ++ break_ctl: at91_break_ctl, ++ startup: at91_startup, ++ shutdown: at91_shutdown, ++ change_speed: at91_change_speed, ++ type: at91_type, ++ release_port: at91_release_port, ++ request_port: at91_request_port, ++ config_port: at91_config_port, ++ verify_port: at91_verify_port, ++}; ++ ++static struct uart_port at91_ports[AT91C_NR_UART]; ++ ++void __init at91_init_ports(void) ++{ ++ static int first = 1; ++ int i; ++ ++ if (!first) ++ return; ++ first = 0; ++ ++ for (i = 0; i < AT91C_NR_UART; i++) { ++ at91_ports[i].iotype = SERIAL_IO_MEM; ++ at91_ports[i].flags = ASYNC_BOOT_AUTOCONF; ++ at91_ports[i].uartclk = AT91C_MASTER_CLOCK; ++ at91_ports[i].ops = &at91_pops; ++ at91_ports[i].fifosize = 1; ++ at91_ports[i].line = i; ++ } ++} ++ ++void __init at91_register_uart_fns(struct at91rm9200_port_fns *fns) ++{ ++ if (fns->enable_ms) ++ at91_pops.enable_ms = fns->enable_ms; ++ if (fns->get_mctrl) ++ at91_pops.get_mctrl = fns->get_mctrl; ++ if (fns->set_mctrl) ++ at91_pops.set_mctrl = fns->set_mctrl; ++ at91_open = fns->open; ++ at91_close = fns->close; ++ at91_pops.pm = fns->pm; ++ at91_pops.set_wake = fns->set_wake; ++} ++ ++/* ++ * Setup ports. ++ */ ++void __init at91_register_uart(int idx, int port) ++{ ++ if ((idx < 0) || (idx >= AT91C_NR_UART)) { ++ printk(KERN_ERR __FUNCTION__ ": bad index number %d\n", idx); ++ return; ++ } ++ ++ switch (port) { ++ case 0: ++ at91_ports[idx].membase = (void *) AT91C_VA_BASE_US0; ++ at91_ports[idx].mapbase = AT91C_VA_BASE_US0; ++ at91_ports[idx].irq = AT91C_ID_US0; ++ AT91_CfgPIO_USART0(); ++ break; ++ case 1: ++ at91_ports[idx].membase = (void *) AT91C_VA_BASE_US1; ++ at91_ports[idx].mapbase = AT91C_VA_BASE_US1; ++ at91_ports[idx].irq = AT91C_ID_US1; ++ AT91_CfgPIO_USART1(); ++ break; ++ case 2: ++ at91_ports[idx].membase = (void *) AT91C_VA_BASE_US2; ++ at91_ports[idx].mapbase = AT91C_VA_BASE_US2; ++ at91_ports[idx].irq = AT91C_ID_US2; ++ AT91_CfgPIO_USART2(); ++ break; ++ case 3: ++ at91_ports[idx].membase = (void *) AT91C_VA_BASE_US3; ++ at91_ports[idx].mapbase = AT91C_VA_BASE_US3; ++ at91_ports[idx].irq = AT91C_ID_US3; ++ AT91_CfgPIO_USART3(); ++ break; ++ case 4: ++ at91_ports[idx].membase = (void *) AT91C_VA_BASE_DBGU; ++ at91_ports[idx].mapbase = AT91C_VA_BASE_DBGU; ++ at91_ports[idx].irq = AT91C_ID_SYS; ++ AT91_CfgPIO_DBGU(); ++ break; ++ default: ++ printk(KERN_ERR __FUNCTION__ ": bad port number %d\n", port); ++ } ++} ++ ++#ifdef CONFIG_SERIAL_AT91_CONSOLE ++ ++/* ++ * Interrupts are disabled on entering ++ */ ++static void at91_console_write(struct console *co, const char *s, u_int count) ++{ ++ struct uart_port *port = at91_ports + co->index; ++ unsigned int status, i, imr; ++ ++ /* ++ * First, save IMR and then disable interrupts ++ */ ++ imr = UART_GET_IMR(port); /* get interrupt mask */ ++ UART_PUT_IDR(port, AT91C_US_RXRDY | AT91C_US_TXRDY); ++ ++ /* ++ * Now, do each character ++ */ ++ for (i = 0; i < count; i++) { ++ do { ++ status = UART_GET_CSR(port); ++ } while (!(status & AT91C_US_TXRDY)); ++ UART_PUT_CHAR(port, s[i]); ++ if (s[i] == '\n') { ++ do { ++ status = UART_GET_CSR(port); ++ } while (!(status & AT91C_US_TXRDY)); ++ UART_PUT_CHAR(port, '\r'); ++ } ++ } ++ ++ /* ++ * Finally, wait for transmitter to become empty ++ * and restore IMR ++ */ ++ do { ++ status = UART_GET_CSR(port); ++ } while (status & AT91C_US_TXRDY); ++ UART_PUT_IER(port, imr); /* set interrupts back the way they were */ ++} ++ ++static kdev_t at91_console_device(struct console *co) ++{ ++ return MKDEV(SERIAL_AT91_MAJOR, MINOR_START + co->index); ++} ++ ++/* ++ * If the port was already initialised (eg, by a boot loader), try to determine ++ * the current setup. ++ */ ++static void __init at91_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) ++{ ++ unsigned int mr, quot; ++ ++// TODO: CR is a write-only register ++// unsigned int cr; ++// ++// cr = UART_GET_CR(port) & (AT91C_US_RXEN | AT91C_US_TXEN); ++// if (cr == (AT91C_US_RXEN | AT91C_US_TXEN)) { ++// /* ok, the port was enabled */ ++// ++// mr = UART_GET_MR(port) & AT91C_US_PAR; ++// ++// *parity = 'n'; ++// if (mr == AT91C_US_PAR_EVEN) ++// *parity = 'e'; ++// else if (mr == AT91C_US_PAR_ODD) ++// *parity = 'o'; ++// } ++ ++ mr = UART_GET_MR(port) & AT91C_US_CHRL; ++ if (mr == AT91C_US_CHRL_8_BITS) ++ *bits = 8; ++ else ++ *bits = 7; ++ ++ quot = UART_GET_BRGR(port); ++ *baud = port->uartclk / (16 * (quot)); ++} ++ ++static int __init at91_console_setup(struct console *co, char *options) ++{ ++ struct uart_port *port; ++ int baud = AT91C_CONSOLE_DEFAULT_BAUDRATE; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++ ++ /* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++ port = uart_get_console(at91_ports, AT91C_NR_UART, co); ++ ++ // TODO: The console port should be initialized, and clock enabled if ++ // we're not relying on the bootloader to do it. ++ ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ else ++ at91_console_get_options(port, &baud, &parity, &bits); ++ ++ return uart_set_options(port, co, baud, parity, bits, flow); ++} ++ ++static struct console at91_console = { ++ name: "ttyS", ++ write: at91_console_write, ++ device: at91_console_device, ++ setup: at91_console_setup, ++ flags: CON_PRINTBUFFER, ++ index: AT91C_CONSOLE, ++}; ++ ++#define AT91_CONSOLE_DEVICE &at91_console ++ ++void __init at91_console_init(void) ++{ ++ at91_init_ports(); ++ register_console(&at91_console); ++} ++ ++#else ++#define AT91_CONSOLE_DEVICE NULL ++#endif ++ ++static struct uart_driver at91_reg = { ++ owner: THIS_MODULE, ++ normal_major: SERIAL_AT91_MAJOR, ++#ifdef CONFIG_DEVFS_FS ++ normal_name: "ttyS%d", ++ callout_name: "cua%d", ++#else ++ normal_name: "ttyS", ++ callout_name: "cua", ++#endif ++ normal_driver: &normal, ++ callout_major: CALLOUT_AT91_MAJOR, ++ callout_driver: &callout, ++ table: at91_table, ++ termios: at91_termios, ++ termios_locked: at91_termios_locked, ++ minor: MINOR_START, ++ nr: AT91C_NR_UART, ++ cons: AT91_CONSOLE_DEVICE, ++}; ++ ++static int __init at91_serial_init(void) ++{ ++ int ret, i; ++ ++ at91_init_ports(); ++ ++ ret = uart_register_driver(&at91_reg); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < AT91C_NR_UART; i++) { ++ if (at91_serialmap[i] >= 0) ++ uart_add_one_port(&at91_reg, &at91_ports[i]); ++ } ++ ++ return 0; ++} ++ ++static void __exit at91_serial_exit(void) ++{ ++ int i; ++ ++ for (i = 0; i < AT91C_NR_UART; i++) { ++ if (at91_serialmap[i] >= 0) ++ uart_remove_one_port(&at91_reg, &at91_ports[i]); ++ } ++ ++ uart_unregister_driver(&at91_reg); ++} ++ ++module_init(at91_serial_init); ++module_exit(at91_serial_exit); ++ ++EXPORT_NO_SYMBOLS; ++ ++MODULE_AUTHOR("Rick Bronson"); ++MODULE_DESCRIPTION("AT91 generic serial port driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/at91/spi/Makefile linux-2.4.26-vrs1/drivers/at91/spi/Makefile +--- linux-2.4.26/drivers/at91/spi/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/spi/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,17 @@ ++# File: drivers/at91/spi/Makefile ++# ++# Makefile for the Atmel AT91RM9200 SPI device drivers ++# ++ ++O_TARGET := at91spi.o ++ ++export-objs := at91_spi.o ++ ++obj-y := at91_spi.o ++obj-m := ++obj-n := ++obj- := ++ ++obj-$(CONFIG_AT91_SPIDEV) += at91_spidev.o ++ ++include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/at91/spi/at91_spi.c linux-2.4.26-vrs1/drivers/at91/spi/at91_spi.c +--- linux-2.4.26/drivers/at91/spi/at91_spi.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/spi/at91_spi.c 2004-04-10 12:23:48.000000000 +0100 +@@ -0,0 +1,275 @@ ++/* ++ * Serial Peripheral Interface (SPI) driver for the Atmel AT91RM9200 (Thunder) ++ * ++ * (c) SAN People (Pty) Ltd ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "at91_spi.h" ++ ++#undef DEBUG_SPI ++ ++static struct spi_local spi_dev[NR_SPI_DEVICES]; /* state of the SPI devices */ ++static int spi_enabled = 0; ++static struct semaphore spi_lock; /* protect access to SPI bus */ ++static int current_device = -1; /* currently selected SPI device */ ++ ++DECLARE_COMPLETION(transfer_complete); ++ ++/* SPI controller device */ ++static AT91PS_SPI controller = (AT91PS_SPI) AT91C_VA_BASE_SPI; ++ ++/* ......................................................................... */ ++ ++/* ++ * Access and enable the SPI bus. ++ * This MUST be called before any transfers are performed. ++ */ ++void spi_access_bus(short device) ++{ ++ /* Ensure that requested device is valid */ ++ if ((device < 0) || (device >= NR_SPI_DEVICES)) ++ panic("at91_spi: spi_access_bus called with invalid device"); ++ ++ if (spi_enabled == 0) { ++ AT91_SYS->PMC_PCER = 1 << AT91C_ID_SPI; /* Enable Peripheral clock */ ++ controller->SPI_CR = AT91C_SPI_SPIEN; /* Enable SPI */ ++#ifdef DEBUG_SPI ++ printk("SPI on\n"); ++#endif ++ } ++ MOD_INC_USE_COUNT; ++ spi_enabled++; ++ ++ /* Lock the SPI bus */ ++ down(&spi_lock); ++ current_device = device; ++ ++ /* Enable PIO */ ++ if (!spi_dev[device].pio_enabled) { ++ switch (device) { ++ case 0: AT91_CfgPIO_SPI_CS0(); ++ case 1: AT91_CfgPIO_SPI_CS1(); ++ case 2: AT91_CfgPIO_SPI_CS2(); ++ case 3: AT91_CfgPIO_SPI_CS3(); ++ } ++ spi_dev[device].pio_enabled = 1; ++#ifdef DEBUG_SPI ++ printk("SPI CS%i enabled\n", device); ++#endif ++ } ++ ++ /* Configure SPI bus for device */ ++ controller->SPI_MR = AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | (spi_dev[device].pcs << 16); ++} ++ ++/* ++ * Relinquish control of the SPI bus. ++ */ ++void spi_release_bus(short device) ++{ ++ if (device != current_device) ++ panic("at91_spi: spi_release called with invalid device"); ++ ++ /* Release the SPI bus */ ++ current_device = -1; ++ up(&spi_lock); ++ ++ spi_enabled--; ++ MOD_DEC_USE_COUNT; ++ if (spi_enabled == 0) { ++ controller->SPI_CR = AT91C_SPI_SPIDIS; /* Disable SPI */ ++ AT91_SYS->PMC_PCER = 1 << AT91C_ID_SPI; /* Disable Peripheral clock */ ++#ifdef DEBUG_SPI ++ printk("SPI off\n"); ++#endif ++ } ++} ++ ++/* ++ * Perform a data transfer over the SPI bus ++ */ ++int spi_transfer(struct spi_transfer_list* list) ++{ ++ struct spi_local *device = (struct spi_local *) &spi_dev[current_device]; ++ ++ if (!list) ++ panic("at91_spi: spi_transfer called with NULL transfer list"); ++ if (current_device == -1) ++ panic("at91_spi: spi_transfer called without acquiring bus"); ++ ++#ifdef DEBUG_SPI ++ printk("SPI transfer start [%i]\n", list->nr_transfers); ++#endif ++ ++ /* Store transfer list */ ++ device->xfers = list; ++ list->curr = 0; ++ ++ /* Assume there must be at least one transfer */ ++ device->tx = pci_map_single(NULL, list->tx[0], list->txlen[0], PCI_DMA_TODEVICE); ++ device->rx = pci_map_single(NULL, list->rx[0], list->rxlen[0], PCI_DMA_FROMDEVICE); ++ ++ /* Program PDC registers */ ++ controller->SPI_TPR = device->tx; ++ controller->SPI_RPR = device->rx; ++ controller->SPI_TCR = list->txlen[0]; ++ controller->SPI_RCR = list->rxlen[0]; ++ ++ /* Is there a second transfer? */ ++ if (list->nr_transfers > 1) { ++ device->txnext = pci_map_single(NULL, list->tx[1], list->txlen[1], PCI_DMA_TODEVICE); ++ device->rxnext = pci_map_single(NULL, list->rx[1], list->rxlen[1], PCI_DMA_FROMDEVICE); ++ ++ /* Program Next PDC registers */ ++ controller->SPI_TNPR = device->txnext; ++ controller->SPI_RNPR = device->rxnext; ++ controller->SPI_TNCR = list->txlen[1]; ++ controller->SPI_RNCR = list->rxlen[1]; ++ } ++ else { ++ device->txnext = 0; ++ device->rxnext = 0; ++ controller->SPI_TNCR = 0; ++ controller->SPI_RNCR = 0; ++ } ++ ++ // TODO: If we are doing consecutive transfers (at high speed, or ++ // small buffers), then it might be worth modifying the 'Delay between ++ // Consecutive Transfers' in the CSR registers. ++ // This is an issue if we cannot chain the next buffer fast enough ++ // in the interrupt handler. ++ ++ /* Enable transmitter and receiver */ ++ controller->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN; ++ ++ controller->SPI_IER = AT91C_SPI_SPENDRX; /* enable buffer complete interrupt */ ++ wait_for_completion(&transfer_complete); ++ ++#ifdef DEBUG_SPI ++ printk("SPI transfer end\n"); ++#endif ++ ++ return 0; ++} ++ ++/* ......................................................................... */ ++ ++/* ++ * Handle interrupts from the SPI controller. ++ */ ++static void spi_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ unsigned int status; ++ struct spi_local *device = (struct spi_local *) &spi_dev[current_device]; ++ struct spi_transfer_list *list = device->xfers; ++ ++#ifdef DEBUG_SPI ++ printk("SPI interrupt %i\n", current_device); ++#endif ++ ++ if (!list) ++ panic("at91_spi: spi_interrupt with a NULL transfer list"); ++ ++ status = controller->SPI_SR & controller->SPI_IMR; /* read status */ ++ ++ pci_unmap_single(NULL, device->tx, list->txlen[list->curr], PCI_DMA_TODEVICE); ++ pci_unmap_single(NULL, device->rx, list->rxlen[list->curr], PCI_DMA_FROMDEVICE); ++ ++ device->tx = device->txnext; /* move next transfer to current transfer */ ++ device->rx = device->rxnext; ++ ++ list->curr = list->curr + 1; ++ if (list->curr == list->nr_transfers) { /* all transfers complete */ ++ controller->SPI_IDR = AT91C_SPI_SPENDRX; /* disable interrupt */ ++ ++ /* Disable transmitter and receiver */ ++ controller->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; ++ ++ device->xfers = NULL; ++ complete(&transfer_complete); ++ } ++ else if (list->curr+1 == list->nr_transfers) { /* no more next transfers */ ++ device->txnext = 0; ++ device->rxnext = 0; ++ controller->SPI_TNCR = 0; ++ controller->SPI_RNCR = 0; ++ } ++ else { ++ int i = (list->curr)+1; ++ ++ device->txnext = pci_map_single(NULL, list->tx[i], list->txlen[i], PCI_DMA_TODEVICE); ++ device->rxnext = pci_map_single(NULL, list->rx[i], list->rxlen[i], PCI_DMA_FROMDEVICE); ++ controller->SPI_TNPR = device->txnext; ++ controller->SPI_RNPR = device->rxnext; ++ controller->SPI_TNCR = list->txlen[i]; ++ controller->SPI_RNCR = list->rxlen[i]; ++ } ++} ++ ++/* ......................................................................... */ ++ ++/* ++ * Initialize the SPI controller ++ */ ++static int __init at91_spi_init(void) ++{ ++ init_MUTEX(&spi_lock); ++ ++ AT91_CfgPIO_SPI(); ++ ++ controller->SPI_CR = AT91C_SPI_SWRST; /* software reset of SPI controller */ ++ ++ /* Set Chip Select registers to good defaults */ ++ controller->SPI_CSR0 = AT91C_SPI_CPOL | AT91C_SPI_BITS_8 | (16 << 16) | (DEFAULT_SPI_BAUD << 8); ++ controller->SPI_CSR1 = AT91C_SPI_CPOL | AT91C_SPI_BITS_8 | (16 << 16) | (DEFAULT_SPI_BAUD << 8); ++ controller->SPI_CSR2 = AT91C_SPI_CPOL | AT91C_SPI_BITS_8 | (16 << 16) | (DEFAULT_SPI_BAUD << 8); ++ controller->SPI_CSR3 = AT91C_SPI_CPOL | AT91C_SPI_BITS_8 | (16 << 16) | (DEFAULT_SPI_BAUD << 8); ++ ++ controller->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; ++ ++ memset(&spi_dev, 0, sizeof(spi_dev)); ++ spi_dev[0].pcs = 0xE; ++ spi_dev[1].pcs = 0xD; ++ spi_dev[2].pcs = 0xB; ++ spi_dev[3].pcs = 0x7; ++ ++ if (request_irq(AT91C_ID_SPI, spi_interrupt, 0, "spi", NULL)) ++ return -EBUSY; ++ ++ controller->SPI_CR = AT91C_SPI_SPIEN; /* Enable SPI */ ++ ++ return 0; ++} ++ ++static void at91_spi_exit(void) ++{ ++ controller->SPI_CR = AT91C_SPI_SPIDIS; /* Disable SPI */ ++ ++ free_irq(AT91C_ID_SPI, 0); ++} ++ ++ ++EXPORT_SYMBOL(spi_access_bus); ++EXPORT_SYMBOL(spi_release_bus); ++EXPORT_SYMBOL(spi_transfer); ++ ++module_init(at91_spi_init); ++module_exit(at91_spi_exit); ++ ++MODULE_LICENSE("GPL") ++MODULE_AUTHOR("Andrew Victor") ++MODULE_DESCRIPTION("SPI driver for Atmel AT91RM9200") +diff -urN linux-2.4.26/drivers/at91/spi/at91_spi.h linux-2.4.26-vrs1/drivers/at91/spi/at91_spi.h +--- linux-2.4.26/drivers/at91/spi/at91_spi.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/spi/at91_spi.h 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,56 @@ ++/* ++ * Serial Peripheral Interface (SPI) driver for the Atmel AT91RM9200 ++ * ++ * (c) SAN People (Pty) Ltd ++ * ++ * 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. ++ */ ++ ++#ifndef AT91_SPI_H ++#define AT91_SPI_H ++ ++/* Maximum number of buffers in a single SPI transfer. ++ * DataFlash uses maximum of 2 ++ * spidev interface supports up to 8. ++ */ ++#define MAX_SPI_TRANSFERS 8 ++ ++#define NR_SPI_DEVICES 4 /* number of devices on SPI bus */ ++ ++#define DATAFLASH_CLK 6000000 ++#define DEFAULT_SPI_BAUD AT91C_MASTER_CLOCK / (2 * DATAFLASH_CLK) ++ ++#define SPI_MAJOR 153 /* registered device number */ ++ ++/* ++ * Describes the buffers for a SPI transfer. ++ * A transmit & receive buffer must be specified for each transfer ++ */ ++struct spi_transfer_list { ++ void* tx[MAX_SPI_TRANSFERS]; /* transmit */ ++ int txlen[MAX_SPI_TRANSFERS]; ++ void* rx[MAX_SPI_TRANSFERS]; /* receive */ ++ int rxlen[MAX_SPI_TRANSFERS]; ++ int nr_transfers; /* number of transfers */ ++ int curr; /* current transfer */ ++}; ++ ++struct spi_local { ++ unsigned int pcs; /* Peripheral Chip Select value */ ++ short pio_enabled; /* has PIO been enabled? */ ++ ++ struct spi_transfer_list *xfers; /* current transfer list */ ++ dma_addr_t tx, rx; /* DMA address for current transfer */ ++ dma_addr_t txnext, rxnext; /* DMA address for next transfer */ ++}; ++ ++ ++/* Exported functions */ ++extern void spi_access_bus(short device); ++extern void spi_release_bus(short device); ++extern int spi_transfer(struct spi_transfer_list* list); ++ ++#endif +diff -urN linux-2.4.26/drivers/at91/spi/at91_spidev.c linux-2.4.26-vrs1/drivers/at91/spi/at91_spidev.c +--- linux-2.4.26/drivers/at91/spi/at91_spidev.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/spi/at91_spidev.c 2004-04-10 12:23:48.000000000 +0100 +@@ -0,0 +1,226 @@ ++/* ++ * User-space interface to the SPI bus on Atmel AT91RM9200 ++ * ++ * (c) SAN People (Pty) Ltd ++ * ++ * Based on SPI driver by Rick Bronson ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_DEVFS_FS ++#include ++#endif ++ ++#include "at91_spi.h" ++ ++#undef DEBUG_SPIDEV ++ ++#ifdef CONFIG_DEVFS_FS ++static devfs_handle_t devfs_handle = NULL; ++static devfs_handle_t devfs_spi[NR_SPI_DEVICES]; ++#endif ++ ++/* ......................................................................... */ ++ ++/* ++ * Read or Write to SPI bus. ++ */ ++static ssize_t spidev_rd_wr(struct file *file, char *buf, size_t count, loff_t *offset) ++{ ++ unsigned int spi_device = (unsigned int) file->private_data; ++ struct kiobuf *iobuf; ++ unsigned int ofs, pagelen; ++ int res, i; ++ ++ struct spi_transfer_list* list = kmalloc(sizeof(struct spi_transfer_list), GFP_KERNEL); ++ if (!list) ++ return -ENOMEM; ++ ++ res = alloc_kiovec(1, &iobuf); ++ if (res) { ++ kfree(list); ++ return res; ++ } ++ ++ res = map_user_kiobuf(READ, iobuf, (unsigned long) buf, count); ++ if (res) { ++ free_kiovec(1, &iobuf); ++ kfree(list); ++ return res; ++ } ++ ++ /* More pages than transfer slots in spi_transfer_list */ ++ if (iobuf->nr_pages >= MAX_SPI_TRANSFERS) { ++ unmap_kiobuf(iobuf); ++ free_kiovec(1, &iobuf); ++ kfree(list); ++ return -EFBIG; ++ } ++ ++#ifdef DEBUG_SPIDEV ++ printk("spidev_rd_rw: %i %i\n", count, iobuf->nr_pages); ++#endif ++ ++ /* Set default return value = transfer length */ ++ res = count; ++ ++ /* ++ * At this point, the virtual area buf[0] .. buf[count-1] will have ++ * corresponding pages mapped in the physical memory and locked until ++ * we unmap the kiobuf. The pages cannot be swapped out or moved ++ * around. ++ */ ++ ofs = iobuf->offset; ++ pagelen = PAGE_SIZE - iobuf->offset; ++ if (count < pagelen) ++ pagelen = count; ++ ++ for (i = 0; i < iobuf->nr_pages; i++) { ++ list->tx[i] = list->rx[i] = page_address(iobuf->maplist[i]) + ofs; ++ list->txlen[i] = list->rxlen[i] = pagelen; ++ ++#ifdef DEBUG_SPIDEV ++ printk(" %i: %x (%i)\n", i, list->tx[i], list->txlen[i]); ++#endif ++ ++ ofs = 0; /* all subsequent transfers start at beginning of a page */ ++ count = count - pagelen; ++ pagelen = (count < PAGE_SIZE) ? count : PAGE_SIZE; ++ } ++ list->nr_transfers = iobuf->nr_pages; ++ ++ /* Perform transfer on SPI bus */ ++ spi_access_bus(spi_device); ++ spi_transfer(list); ++ spi_release_bus(spi_device); ++ ++ unmap_kiobuf(iobuf); ++ free_kiovec(1, &iobuf); ++ kfree(list); ++ ++ return res; ++} ++ ++static int spidev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int spi_device = MINOR(inode->i_rdev); ++ ++ if (spi_device >= NR_SPI_DEVICES) ++ return -ENODEV; ++ ++ // TODO: This interface can be used to configure the SPI bus. ++ // Configurable options could include: Speed, Clock Polarity, Clock Phase ++ ++ switch(cmd) { ++ default: ++ return -ENOIOCTLCMD; ++ } ++} ++ ++/* ++ * Open the SPI device ++ */ ++static int spidev_open(struct inode *inode, struct file *file) ++{ ++ unsigned int spi_device = MINOR(inode->i_rdev); ++ ++ if (spi_device >= NR_SPI_DEVICES) ++ return -ENODEV; ++ ++ MOD_INC_USE_COUNT; ++ ++ /* ++ * 'private_data' is actually a pointer, but we overload it with the ++ * value we want to store. ++ */ ++ (unsigned int) file->private_data = spi_device; ++ ++ return 0; ++} ++ ++/* ++ * Close the SPI device ++ */ ++static int spidev_close(struct inode *inode, struct file *file) ++{ ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++/* ......................................................................... */ ++ ++static struct file_operations spidev_fops = { ++ owner: THIS_MODULE, ++ llseek: no_llseek, ++ read: spidev_rd_wr, ++ write: spidev_rd_wr, ++ ioctl: spidev_ioctl, ++ open: spidev_open, ++ release: spidev_close, ++}; ++ ++/* ++ * Install the SPI /dev interface driver ++ */ ++static int __init at91_spidev_init(void) ++{ ++ int i; ++ char name[3]; ++ ++#ifdef CONFIG_DEVFS_FS ++ if (devfs_register_chrdev(SPI_MAJOR, "spi", &spidev_fops)) { ++#else ++ if (register_chrdev(SPI_MAJOR, "spi", &spidev_fops)) { ++#endif ++ printk(KERN_ERR "at91_spidev: Unable to get major %d for SPI bus\n", SPI_MAJOR); ++ return -EIO; ++ } ++ ++#ifdef CONFIG_DEVFS_FS ++ devfs_handle = devfs_mk_dir(NULL, "spi", NULL); ++ ++ for (i = 0; i < NR_SPI_DEVICES; i++) { ++ sprintf (name, "%d", i); ++ devfs_spi[i] = devfs_register (devfs_handle, name, ++ DEVFS_FL_DEFAULT, SPI_MAJOR, i, S_IFCHR | S_IRUSR | S_IWUSR, ++ &spidev_fops, NULL); ++ } ++#endif ++ printk(KERN_INFO "AT91 SPI driver loaded\n"); ++ ++ return 0; ++} ++ ++/* ++ * Remove the SPI /dev interface driver ++ */ ++static void __exit at91_spidev_exit(void) ++{ ++#ifdef CONFIG_DEVFS_FS ++ devfs_unregister(devfs_handle); ++ if (devfs_unregister_chrdev(SPI_MAJOR, "spi")) { ++#else ++ if (unregister_chrdev(SPI_MAJOR,"spi")) { ++#endif ++ printk(KERN_ERR "at91_spidev: Unable to release major %d for SPI bus\n", SPI_MAJOR); ++ return; ++ } ++} ++ ++module_init(at91_spidev_init); ++module_exit(at91_spidev_exit); ++ ++MODULE_LICENSE("GPL") ++MODULE_AUTHOR("Andrew Victor") ++MODULE_DESCRIPTION("SPI /dev interface for Atmel AT91RM9200") +diff -urN linux-2.4.26/drivers/at91/usb/Makefile linux-2.4.26-vrs1/drivers/at91/usb/Makefile +--- linux-2.4.26/drivers/at91/usb/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/usb/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,17 @@ ++# File: drivers/at91/usb/Makefile ++# ++# Makefile for the Atmel AT91RM9200 USB device drivers ++# ++ ++O_TARGET := at91usb.o ++ ++export-objs := ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++obj-$(CONFIG_USB_OHCI_AT91) += at91_usb-ohci.o ++ ++include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/at91/usb/at91_usb-ohci.c linux-2.4.26-vrs1/drivers/at91/usb/at91_usb-ohci.c +--- linux-2.4.26/drivers/at91/usb/at91_usb-ohci.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/usb/at91_usb-ohci.c 2004-04-10 12:44:07.000000000 +0100 +@@ -0,0 +1,85 @@ ++/* ++ * linux/drivers/at91/usb/at91_usb_ohci-at91.c ++ * ++ * (c) Rick Bronson ++ * ++ * The outline of this code was taken from Brad Parkers ++ * original OHCI driver modifications, and reworked into a cleaner form ++ * by Russell King . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* ++ NOTE: ++ The following is so that we don't have to include usb-ohci.h or pci.h as the ++ usb-ohci.c driver needs these routines even when the architecture ++ has no PCI bus... ++*/ ++ ++extern int __devinit hc_add_ohci(struct pci_dev *dev, int irq, void *membase, ++ unsigned long flags, void *ohci, const char *name, ++ const char *slot_name); ++extern void hc_remove_ohci(void *ohci); ++ ++static void *at91_ohci; ++AT91PS_UHP ohci_regs; ++ ++static int __init at91_ohci_init(void) ++{ ++ int ret; ++ ++ ohci_regs = ioremap(AT91_UHP_BASE, SZ_4K); ++ if (!ohci_regs) { ++ printk(KERN_ERR "at91_usb-ohci: ioremap failed\n"); ++ return -EIO; ++ } ++ ++ /* Enable PLLB */ ++ AT91_SYS->CKGR_PLLBR = AT91_PLLB_INIT; ++ while ((AT91_SYS->PMC_SR & 4) == 0); ++ ++ /* Now, enable the USB clock */ ++ AT91_SYS->PMC_SCER = AT91C_PMC_UHP; /* enable system clock */ ++ AT91_SYS->PMC_PCER = 1 << AT91C_ID_UHP; /* enable peripheral clock */ ++ ++ /* Take Hc out of reset */ ++ ohci_regs->UHP_HcControl = 2 << 6; ++ ++ /* Initialise the generic OHCI driver. */ ++ ret = hc_add_ohci((struct pci_dev *) 1, AT91C_ID_UHP, ++ (void *)ohci_regs, 0, &at91_ohci, ++ "usb-ohci", "at91"); ++ if (ret) ++ iounmap(ohci_regs); ++ ++ return ret; ++} ++ ++static void __exit at91_ohci_exit(void) ++{ ++ hc_remove_ohci(at91_ohci); ++ ++ /* Force UHP_Hc to reset */ ++ ohci_regs->UHP_HcControl = 0; ++ ++ /* Stop the USB clock. */ ++ AT91_SYS->PMC_SCDR = AT91C_PMC_UHP; /* disable system clock */ ++ AT91_SYS->PMC_PCDR = 1 << AT91C_ID_UHP; /* disable peripheral clock */ ++ ++ iounmap(ohci_regs); ++} ++ ++module_init(at91_ohci_init); ++module_exit(at91_ohci_exit); +diff -urN linux-2.4.26/drivers/at91/watchdog/Makefile linux-2.4.26-vrs1/drivers/at91/watchdog/Makefile +--- linux-2.4.26/drivers/at91/watchdog/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/watchdog/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,15 @@ ++# File: drivers/at91/watchdog/Makefile ++# ++# Makefile for the Atmel AT91RM9200 watchdog device driver ++# ++ ++O_TARGET := at91wdt.o ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++obj-$(CONFIG_AT91_WATCHDOG) += at91_wdt.o ++ ++include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/at91/watchdog/at91_wdt.c linux-2.4.26-vrs1/drivers/at91/watchdog/at91_wdt.c +--- linux-2.4.26/drivers/at91/watchdog/at91_wdt.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/at91/watchdog/at91_wdt.c 2004-04-10 12:23:48.000000000 +0100 +@@ -0,0 +1,193 @@ ++/* ++ * Watchdog driver for Atmel AT91RM9200 (Thunder) ++ * ++ * (c) SAN People (Pty) Ltd ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define WDT_DEFAULT_TIME 5 /* 5 seconds */ ++#define WDT_MAX_TIME 256 /* 256 seconds */ ++ ++static int at91wdt_time = WDT_DEFAULT_TIME; ++static int at91wdt_busy; ++ ++/* ......................................................................... */ ++ ++/* ++ * Disable the watchdog. ++ */ ++static void at91_wdt_stop(void) ++{ ++ AT91_SYS->ST_WDMR = AT91C_ST_EXTEN; ++} ++ ++/* ++ * Enable and reset the watchdog. ++ */ ++static void at91_wdt_start(void) ++{ ++ AT91_SYS->ST_WDMR = AT91C_ST_EXTEN | AT91C_ST_RSTEN | (((65536 * at91wdt_time) >> 8) & AT91C_ST_WDV); ++ AT91_SYS->ST_CR = AT91C_ST_WDRST; ++} ++ ++/* ......................................................................... */ ++ ++/* ++ * Watchdog device is opened, and watchdog starts running. ++ */ ++static int at91_wdt_open(struct inode *inode, struct file *file) ++{ ++ if (test_and_set_bit(1, &at91wdt_busy)) ++ return -EBUSY; ++ MOD_INC_USE_COUNT; ++ ++ /* ++ * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz ++ * ++ * Since WDV is a 16-bit counter, the maximum period is ++ * 65536 / 0.256 = 256 seconds. ++ */ ++ ++ at91_wdt_start(); ++ return 0; ++} ++ ++/* ++ * Close the watchdog device. ++ * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also ++ * disabled. ++ */ ++static int at91_wdt_close(struct inode *inode, struct file *file) ++{ ++#ifndef CONFIG_WATCHDOG_NOWAYOUT ++ /* Disable the watchdog when file is closed */ ++ at91_wdt_stop(); ++#endif ++ ++ at91wdt_busy = 0; ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++/* ++ * Handle commands from user-space. ++ */ ++static int at91_wdt_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ unsigned int new_value; ++ static struct watchdog_info info = { ++ identity: "at91 watchdog", ++ options: WDIOF_SETTIMEOUT, ++ }; ++ ++ switch(cmd) { ++ case WDIOC_KEEPALIVE: ++ AT91_SYS->ST_CR = AT91C_ST_WDRST; /* Pat the watchdog */ ++ return 0; ++ ++ case WDIOC_GETSUPPORT: ++ return copy_to_user((struct watchdog_info *)arg, &info, sizeof(info)); ++ ++ case WDIOC_SETTIMEOUT: ++ if (get_user(new_value, (int *)arg)) ++ return -EFAULT; ++ if ((new_value <= 0) || (new_value > WDT_MAX_TIME)) ++ return -EINVAL; ++ ++ /* Restart watchdog with new time */ ++ at91wdt_time = new_value; ++ at91_wdt_start(); ++ ++ /* Return current value */ ++ return put_user(at91wdt_time, (int *)arg); ++ ++ case WDIOC_GETTIMEOUT: ++ return put_user(at91wdt_time, (int *)arg); ++ ++ case WDIOC_GETSTATUS: ++ return put_user(0, (int *)arg); ++ ++ case WDIOC_SETOPTIONS: ++ if (get_user(new_value, (int *)arg)) ++ return -EFAULT; ++ if (new_value & WDIOS_DISABLECARD) ++ at91_wdt_stop(); ++ if (new_value & WDIOS_ENABLECARD) ++ at91_wdt_start(); ++ return 0; ++ ++ default: ++ return -ENOIOCTLCMD; ++ } ++} ++ ++/* ++ * Pat the watchdog whenever device is written to. ++ */ ++static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) ++{ ++ /* Can't seek (pwrite) on this device */ ++ if (ppos != &file->f_pos) ++ return -ESPIPE; ++ ++ if (len) { ++ AT91_SYS->ST_CR = AT91C_ST_WDRST; /* Pat the watchdog */ ++ return len; ++ } ++ ++ return 0; ++} ++ ++/* ......................................................................... */ ++ ++static struct file_operations at91wdt_fops = ++{ ++ .owner = THIS_MODULE, ++ .ioctl = at91_wdt_ioctl, ++ .open = at91_wdt_open, ++ .release = at91_wdt_close, ++ .write = at91_wdt_write, ++}; ++ ++static struct miscdevice at91wdt_miscdev = ++{ ++ .minor = WATCHDOG_MINOR, ++ .name = "watchdog", ++ .fops = &at91wdt_fops, ++}; ++ ++static int __init at91_wdt_init(void) ++{ ++ int res; ++ ++ res = misc_register(&at91wdt_miscdev); ++ if (res) ++ return res; ++ ++ printk("AT91 Watchdog Timer enabled (%d seconds)\n", WDT_DEFAULT_TIME); ++ return 0; ++} ++ ++static void __exit at91_wdt_exit(void) ++{ ++ misc_deregister(&at91wdt_miscdev); ++} ++ ++module_init(at91_wdt_init); ++module_exit(at91_wdt_exit); ++ ++MODULE_LICENSE("GPL") ++MODULE_AUTHOR("Andrew Victor") ++MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200") +diff -urN linux-2.4.26/drivers/block/Makefile linux-2.4.26-vrs1/drivers/block/Makefile +--- linux-2.4.26/drivers/block/Makefile 2003-06-13 15:51:32.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/block/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -27,11 +27,17 @@ + obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o + obj-$(CONFIG_BLK_DEV_XD) += xd.o + obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o +-obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o ++obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o + obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o + obj-$(CONFIG_BLK_DEV_UMEM) += umem.o + obj-$(CONFIG_BLK_DEV_NBD) += nbd.o + + subdir-$(CONFIG_PARIDE) += paride + ++ifeq ($(CONFIG_ARCH_ACORN),y) ++mod-subdirs += ../acorn/block ++subdir-y += ../acorn/block ++obj-y += ../acorn/block/acorn-block.o ++endif ++ + include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/block/ll_rw_blk.c linux-2.4.26-vrs1/drivers/block/ll_rw_blk.c +--- linux-2.4.26/drivers/block/ll_rw_blk.c 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/block/ll_rw_blk.c 2004-04-18 21:47:50.000000000 +0100 +@@ -32,6 +32,19 @@ + #include + #include + ++/* Maybe something to cleanup in 2.3? ++ * We shouldn't touch 0x3f2 on machines which don't have a PC floppy controller ++ * - it may contain something else which could cause a system hang. This is ++ * now selected by a configuration option, but maybe it ought to be in the ++ * floppy code itself? - rmk ++ */ ++#if defined(__i386__) || (defined(__arm__) && defined(CONFIG_ARCH_ACORN)) ++#define FLOPPY_BOOT_DISABLE ++#endif ++#ifdef CONFIG_BLK_DEV_FD ++#undef FLOPPY_BOOT_DISABLE ++#endif ++ + /* + * MAC Floppy IWM hooks + */ +@@ -524,7 +537,7 @@ + elevator_init(&q->elevator, ELEVATOR_LINUS); + blk_init_free_list(q); + q->request_fn = rfn; +- q->back_merge_fn = ll_back_merge_fn; ++ q->back_merge_fn = ll_back_merge_fn; + q->front_merge_fn = ll_front_merge_fn; + q->merge_requests_fn = ll_merge_requests_fn; + q->make_request_fn = __make_request; +@@ -1549,7 +1562,7 @@ + mfm_init(); + #endif + #ifdef CONFIG_PARIDE +- { extern void paride_init(void); paride_init(); }; ++ { extern void paride_init(void); paride_init(); } + #endif + #ifdef CONFIG_MAC_FLOPPY + swim3_init(); +@@ -1563,12 +1576,14 @@ + #ifdef CONFIG_ATARI_FLOPPY + atari_floppy_init(); + #endif ++#ifdef CONFIG_BLK_DEV_FD1772 ++ fd1772_init(); ++#endif + #ifdef CONFIG_BLK_DEV_FD + floppy_init(); +-#else +-#if defined(__i386__) /* Do we even need this? */ +- outb_p(0xc, 0x3f2); + #endif ++#ifdef FLOPPY_BOOT_DISABLE ++ outb_p(0xc, 0x3f2); + #endif + #ifdef CONFIG_CDU31A + cdu31a_init(); +@@ -1626,7 +1641,7 @@ + jsfd_init(); + #endif + return 0; +-}; ++} + + EXPORT_SYMBOL(io_request_lock); + EXPORT_SYMBOL(end_that_request_first); +diff -urN linux-2.4.26/drivers/cdrom/cdrom.c linux-2.4.26-vrs1/drivers/cdrom/cdrom.c +--- linux-2.4.26/drivers/cdrom/cdrom.c 2003-11-28 18:26:20.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/cdrom/cdrom.c 2004-01-14 21:38:59.000000000 +0000 +@@ -246,8 +246,8 @@ + #define CD_DVD 0x80 + + /* Define this to remove _all_ the debugging messages */ +-/* #define ERRLOGMASK CD_NOTHING */ +-#define ERRLOGMASK (CD_WARNING) ++#define ERRLOGMASK CD_NOTHING ++/* #define ERRLOGMASK (CD_WARNING) */ + /* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_COUNT_TRACKS|CD_CLOSE) */ + /* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE|CD_COUNT_TRACKS) */ + +diff -urN linux-2.4.26/drivers/char/Config.in linux-2.4.26-vrs1/drivers/char/Config.in +--- linux-2.4.26/drivers/char/Config.in 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/char/Config.in 2004-04-10 12:04:15.000000000 +0100 +@@ -20,10 +20,10 @@ + if [ "$CONFIG_IA64" = "y" ]; then + bool ' Support for serial port described by EFI HCDP table' CONFIG_SERIAL_HCDP + fi +- if [ "$CONFIG_ARCH_ACORN" = "y" ]; then +- tristate ' Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL +- tristate ' Dual serial port support' CONFIG_DUALSP_SERIAL +- fi ++fi ++if [ "$CONFIG_ARCH_ACORN" = "y" ]; then ++ dep_tristate ' Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL $CONFIG_SERIAL ++ dep_tristate ' Dual serial port support' CONFIG_DUALSP_SERIAL $CONFIG_SERIAL + fi + dep_mbool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED $CONFIG_SERIAL + if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then +@@ -132,18 +132,6 @@ + bool ' SGI SN2 IOC4 serial port support' CONFIG_SGI_IOC4_SERIAL + fi + fi +-fi +-if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_ZORRO" = "y" ]; then +- tristate 'Commodore A2232 serial support (EXPERIMENTAL)' CONFIG_A2232 +-fi +-if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then +- bool 'DC21285 serial port support' CONFIG_SERIAL_21285 +- if [ "$CONFIG_SERIAL_21285" = "y" ]; then +- if [ "$CONFIG_OBSOLETE" = "y" ]; then +- bool ' Use /dev/ttyS0 device (OBSOLETE)' CONFIG_SERIAL_21285_OLD +- fi +- bool ' Console on DC21285 serial port' CONFIG_SERIAL_21285_CONSOLE +- fi + if [ "$CONFIG_PARISC" = "y" ]; then + bool ' PDC software console support' CONFIG_PDC_CONSOLE + fi +@@ -168,6 +156,16 @@ + if [ "$CONFIG_CPU_VR41XX" = "y" ]; then + bool 'NEC VR4100 series Keyboard Interface Unit Support ' CONFIG_VR41XX_KIU + fi ++if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then ++ tristate 'AT91RM9200 SPI device interface' CONFIG_AT91_SPIDEV ++fi ++ ++source drivers/serial/Config.in ++ ++if [ "$CONFIG_ARCH_ANAKIN" = "y" ]; then ++ tristate 'Anakin touchscreen support' CONFIG_TOUCHSCREEN_ANAKIN ++fi ++ + bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS + if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then + int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 +@@ -190,6 +188,12 @@ + + source drivers/i2c/Config.in + ++if [ "$CONFIG_I2C" != "n" ]; then ++ dep_tristate ' DS1307 RTC' CONFIG_I2C_DS1307 $CONFIG_I2C ++fi ++ ++source drivers/l3/Config.in ++ + mainmenu_option next_comment + comment 'Mice' + tristate 'Bus Mouse Support' CONFIG_BUSMOUSE +@@ -245,11 +249,13 @@ + tristate ' ALi M7101 PMU Watchdog Timer' CONFIG_ALIM7101_WDT + tristate ' AMD "Elan" SC520 Watchdog Timer' CONFIG_SC520_WDT + tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG +- if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then +- tristate ' DC21285 watchdog' CONFIG_21285_WATCHDOG +- if [ "$CONFIG_ARCH_NETWINDER" = "y" ]; then +- tristate ' NetWinder WB83C977 watchdog' CONFIG_977_WATCHDOG +- fi ++ if [ "$CONFIG_ARM" = "y" ]; then ++ dep_tristate ' DC21285 watchdog' CONFIG_21285_WATCHDOG $CONFIG_FOOTBRIDGE ++ dep_tristate ' NetWinder WB83C977 watchdog' CONFIG_977_WATCHDOG $CONFIG_ARCH_NETWINDER ++ dep_tristate ' SA1100 watchdog' CONFIG_SA1100_WATCHDOG $CONFIG_ARCH_SA1100 ++ dep_tristate ' EPXA watchdog' CONFIG_EPXA_WATCHDOG $CONFIG_ARCH_CAMELOT ++ dep_tristate ' Omaha watchdog' CONFIG_OMAHA_WATCHDOG $CONFIG_ARCH_OMAHA ++ dep_tristate ' AT91RM9200 watchdog' CONFIG_AT91_WATCHDOG $CONFIG_ARCH_AT91RM9200 + fi + tristate ' Eurotech CPU-1220/1410 Watchdog Timer' CONFIG_EUROTECH_WDT + tristate ' IB700 SBC Watchdog Timer' CONFIG_IB700_WDT +@@ -326,6 +332,15 @@ + if [ "$CONFIG_TOSHIBA_RBTX4927" = "y" -o "$CONFIG_TOSHIBA_JMR3927" = "y" ]; then + tristate 'Dallas DS1742 RTC support' CONFIG_DS1742 + fi ++if [ "$CONFIG_ARCH_SA1100" = "y" ]; then ++ tristate 'SA1100 Real Time Clock' CONFIG_SA1100_RTC ++fi ++if [ "$CONFIG_ARCH_OMAHA" = "y" ]; then ++ tristate 'Omaha Real Time Clock' CONFIG_OMAHA_RTC ++fi ++if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then ++ tristate 'AT91RM9200 Real Time Clock' CONFIG_AT91_RTC ++fi + + tristate 'Double Talk PC internal speech card support' CONFIG_DTLK + tristate 'Siemens R3964 line discipline' CONFIG_R3964 +diff -urN linux-2.4.26/drivers/char/Makefile linux-2.4.26-vrs1/drivers/char/Makefile +--- linux-2.4.26/drivers/char/Makefile 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/char/Makefile 2004-04-10 12:07:34.000000000 +0100 +@@ -29,7 +29,7 @@ + + mod-subdirs := joystick ftape drm drm-4.0 pcmcia + +-list-multi := ++list-multi := + + KEYMAP =defkeymap.o + KEYBD =pc_keyb.o +@@ -106,11 +106,39 @@ + endif + + ifeq ($(ARCH),arm) +- ifneq ($(CONFIG_PC_KEYMAP),y) +- KEYMAP = ++ KEYMAP := ++ KEYBD := ++ ifeq ($(CONFIG_PC_KEYMAP),y) ++ KEYMAP := defkeymap.o + endif +- ifneq ($(CONFIG_PC_KEYB),y) +- KEYBD = ++ ifeq ($(CONFIG_PC_KEYB),y) ++ KEYBD += pc_keyb.o ++ endif ++ ifeq ($(CONFIG_KMI_KEYB),y) ++ KEYBD += amba_kmi_keyb.o ++ endif ++ ifeq ($(CONFIG_SA1111),y) ++ KEYBD += sa1111_keyb.o ++ endif ++ ifeq ($(CONFIG_ARCH_EDB7211),y) ++ KEYBD += edb7211_keyb.o ++ endif ++ ifeq ($(CONFIG_ARCH_AUTCPU12),y) ++ KEYMAP := defkeymap.o ++ KEYBD += clps711x_keyb.o ++ endif ++ ifeq ($(CONFIG_SA1100_GRAPHICSCLIENT),y) ++ KEYMAP = gckeymap.o ++ KEYBD += gc_keyb.o ++ endif ++ ifeq ($(CONFIG_SA1100_CERF_CPLD),y) ++ KEYBD += cerf_keyb.o ++ endif ++ ifeq ($(CONFIG_ARCH_FORTUNET),y) ++ KEYMAP := defkeymap.o ++ endif ++ ifeq ($(CONFIG_ARCH_GUIDEA07),y) ++ KEYMAP := defkeymap.o + endif + endif + +@@ -172,11 +200,9 @@ + obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o + obj-$(CONFIG_SERIAL) += $(SERIAL) + obj-$(CONFIG_SERIAL_HCDP) += hcdp_serial.o +-obj-$(CONFIG_SERIAL_21285) += serial_21285.o +-obj-$(CONFIG_SERIAL_SA1100) += serial_sa1100.o +-obj-$(CONFIG_SERIAL_AMBA) += serial_amba.o + obj-$(CONFIG_TS_AU1X00_ADS7846) += au1000_ts.o + obj-$(CONFIG_SERIAL_DEC) += decserial.o ++obj-$(CONFIG_TOUCHSCREEN_ANAKIN) += anakin_ts.o + + ifndef CONFIG_SUN_KEYBOARD + obj-$(CONFIG_VT) += keyboard.o $(KEYMAP) $(KEYBD) +@@ -253,6 +279,8 @@ + obj-$(CONFIG_SGI_DS1286) += ds1286.o + obj-$(CONFIG_MIPS_RTC) += mips_rtc.o + obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o ++obj-$(CONFIG_SA1100_RTC) += sa1100-rtc.o ++obj-$(CONFIG_OMAHA_RTC) += omaha-rtc.o + ifeq ($(CONFIG_PPC),) + obj-$(CONFIG_NVRAM) += nvram.o + endif +@@ -291,6 +319,7 @@ + obj-$(CONFIG_NWFLASH) += nwflash.o + obj-$(CONFIG_SCx200) += scx200.o + obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o ++obj-$(CONFIG_SA1100_CONSUS) += consusbutton.o + + # Only one watchdog can succeed. We probe the hardware watchdog + # drivers first, then the softdog driver. This means if your hardware +@@ -319,16 +348,28 @@ + obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o + obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o + obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o ++obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o ++obj-$(CONFIG_EPXA_WATCHDOG) += epxa_wdt.o ++obj-$(CONFIG_OMAHA_WATCHDOG) += omaha_wdt.o + obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o + obj-$(CONFIG_AMD7XX_TCO) += amd7xx_tco.o + obj-$(CONFIG_INDYDOG) += indydog.o + obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o + ++# I2C char devices ++obj-$(CONFIG_I2C_DS1307) += ds1307.o ++ + subdir-$(CONFIG_MWAVE) += mwave + ifeq ($(CONFIG_MWAVE),y) + obj-y += mwave/mwave.o + endif + ++ifeq ($(CONFIG_ARCH_ACORN),y) ++mod-subdirs += ../acorn/char ++subdir-y += ../acorn/char ++obj-y += ../acorn/char/acorn-char.o ++endif ++ + subdir-$(CONFIG_IPMI_HANDLER) += ipmi + ifeq ($(CONFIG_IPMI_HANDLER),y) + obj-y += ipmi/ipmi.o +diff -urN linux-2.4.26/drivers/char/amba_kmi_keyb.c linux-2.4.26-vrs1/drivers/char/amba_kmi_keyb.c +--- linux-2.4.26/drivers/char/amba_kmi_keyb.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/amba_kmi_keyb.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,999 @@ ++/* ++ * linux/drivers/char/amba_kmi_keyb.c ++ * ++ * AMBA Keyboard and Mouse Interface Driver ++ * ++ * Copyright (C) 2000 Deep Blue Solutions Ltd. ++ * ++ * 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 ++ * ++ * This keyboard driver drives a PS/2 keyboard and mouse connected ++ * to the KMI interfaces. The KMI interfaces are nothing more than ++ * a uart; there is no inteligence in them to do keycode translation. ++ * We leave all that up to the keyboard itself. ++ * ++ * FIXES: ++ * dirk.uffmann@nokia.com: enabled PS/2 reconnection ++ */ ++#include ++#include ++#include ++#include /* for in_interrupt */ ++#include ++#include ++#include /* for udelay */ ++#include /* for keyboard_tasklet */ ++#include ++ ++#include ++#include ++#include ++#include ++ ++//#define DEBUG(s) printk s ++#define DEBUG(s) do { } while (0) ++ ++#define CONFIG_AMBA_PS2_RECONNECT ++ ++#define KMI_BASE (kmi->base) ++ ++#define KMI_RESET 0x00 ++#define KMI_RESET_POR 0x01 ++#define KMI_RESET_DONE 0x02 ++ ++#define KMI_NO_ACK 0xffff ++ ++#define PS2_O_RESET 0xff ++#define PS2_O_RESEND 0xfe ++#define PS2_O_DISABLE 0xf5 ++#define PS2_O_ENABLE 0xf4 ++#define PS2_O_ECHO 0xee ++ ++/* ++ * Keyboard ++ */ ++#define PS2_O_SET_DEFAULT 0xf6 ++#define PS2_O_SET_RATE_DELAY 0xf3 ++#define PS2_O_SET_SCANSET 0xf0 ++#define PS2_O_INDICATORS 0xed ++ ++/* ++ * Mouse ++ */ ++#define PS2_O_SET_SAMPLE 0xf3 ++#define PS2_O_SET_STREAM 0xea ++#define PS2_O_SET_RES 0xe8 ++#define PS2_O_SET_SCALE21 0xe7 ++#define PS2_O_SET_SCALE11 0xe6 ++#define PS2_O_REQ_STATUS 0xe9 ++ ++/* ++ * Responses ++ */ ++#define PS2_I_RESEND 0xfe ++#define PS2_I_DIAGFAIL 0xfc ++#define PS2_I_ACK 0xfa ++#define PS2_I_BREAK 0xf0 ++#define PS2_I_ECHO 0xee ++#define PS2_I_BAT_OK 0xaa ++ ++static char *kmi_type[] = { "Keyboard", "Mouse" }; ++ ++static struct kmi_info *kmi_keyb; ++static struct kmi_info *kmi_mouse; ++ ++static inline void __kmi_send(struct kmi_info *kmi, u_int val) ++{ ++ u_int status; ++ ++ do { ++ status = __raw_readb(KMISTAT); ++ } while (!(status & KMISTAT_TXEMPTY)); ++ ++ kmi->resend_count += 1; ++ __raw_writeb(val, KMIDATA); ++} ++ ++static void kmi_send(struct kmi_info *kmi, u_int val) ++{ ++ kmi->last_tx = val; ++ kmi->resend_count = -1; ++ __kmi_send(kmi, val); ++} ++ ++static u_int kmi_send_and_wait(struct kmi_info *kmi, u_int val, u_int timeo) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ ++ if (kmi->present == 0) ++ return KMI_NO_ACK; ++ ++ kmi->res = KMI_NO_ACK; ++ kmi->last_tx = val; ++ kmi->resend_count = -1; ++ ++ if (current->pid != 0 && !in_interrupt()) { ++ add_wait_queue(&kmi->wait_q, &wait); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ __kmi_send(kmi, val); ++ schedule_timeout(timeo); ++ current->state = TASK_RUNNING; ++ remove_wait_queue(&kmi->wait_q, &wait); ++ } else { ++ int i; ++ ++ __kmi_send(kmi, val); ++ for (i = 0; i < 1000; i++) { ++ if (kmi->res != KMI_NO_ACK) ++ break; ++ udelay(100); ++ } ++ } ++ ++ return kmi->res; ++} ++ ++/* ++ * This lot should probably be separated into a separate file... ++ */ ++#ifdef CONFIG_KMI_MOUSE ++ ++#include /* for struct file_ops */ ++#include /* for poll_table */ ++#include /* for struct miscdev */ ++#include /* for add_mouse_randomness */ ++#include /* for kmalloc */ ++#include /* for {un,}lock_kernel */ ++#include ++ ++#include ++ ++#define BUF_SZ 2048 ++ ++static spinlock_t kmi_mouse_lock; ++static int kmi_mouse_count; ++static struct queue { ++ u_int head; ++ u_int tail; ++ struct fasync_struct *fasync; ++ unsigned char buf[BUF_SZ]; ++} *queue; ++ ++#define queue_empty() (queue->head == queue->tail) ++ ++static u_char get_from_queue(void) ++{ ++ unsigned long flags; ++ u_char res; ++ ++ spin_lock_irqsave(&kmi_mouse_lock, flags); ++ res = queue->buf[queue->tail]; ++ queue->tail = (queue->tail + 1) & (BUF_SZ-1); ++ spin_unlock_irqrestore(&kmi_mouse_lock, flags); ++ ++ return res; ++} ++ ++static ssize_t ++kmi_mouse_read(struct file *file, char *buf, size_t count, loff_t *ppos) ++{ ++ ssize_t i = count; ++ ++ if (queue_empty()) { ++ int ret; ++ ++ if (file->f_flags & O_NONBLOCK) ++ return -EAGAIN; ++ ret = wait_event_interruptible(kmi_mouse->wait_q, !queue_empty()); ++ if (ret) ++ return ret; ++ } ++ while (i > 0 && !queue_empty()) { ++ u_char c; ++ c = get_from_queue(); ++ put_user(c, buf++); ++ i--; ++ } ++ if (count - i) ++ file->f_dentry->d_inode->i_atime = CURRENT_TIME; ++ return count - i; ++} ++ ++static ssize_t ++kmi_mouse_write(struct file *file, const char *buf, size_t count, loff_t *ppos) ++{ ++ ssize_t retval = 0; ++ ++ if (count > 32) ++ count = 32; ++ ++ do { ++ char c; ++ get_user(c, buf++); ++ kmi_send_and_wait(kmi_mouse, c, HZ); ++ retval++; ++ } while (--count); ++ ++ if (retval) ++ file->f_dentry->d_inode->i_mtime = CURRENT_TIME; ++ ++ return retval; ++} ++ ++static unsigned int ++kmi_mouse_poll(struct file *file, poll_table *wait) ++{ ++ poll_wait(file, &kmi_mouse->wait_q, wait); ++ return (!queue_empty()) ? POLLIN | POLLRDNORM : 0; ++} ++ ++static int ++kmi_mouse_release(struct inode *inode, struct file *file) ++{ ++ lock_kernel(); ++ fasync_helper(-1, file, 0, &queue->fasync); ++ if (--kmi_mouse_count == 0) ++ kmi_send_and_wait(kmi_mouse, PS2_O_DISABLE, HZ); ++ unlock_kernel(); ++ return 0; ++} ++ ++static int ++kmi_mouse_open(struct inode *inode, struct file *file) ++{ ++ if (kmi_mouse_count++) ++ return 0; ++ queue->head = queue->tail = 0; ++ kmi_send_and_wait(kmi_mouse, PS2_O_ENABLE, HZ); ++ return 0; ++} ++ ++static int ++kmi_mouse_fasync(int fd, struct file *filp, int on) ++{ ++ int retval = fasync_helper(fd, filp, on, &queue->fasync); ++ if (retval > 0) ++ retval = 0; ++ return retval; ++} ++ ++static struct file_operations ps_fops = { ++ read: kmi_mouse_read, ++ write: kmi_mouse_write, ++ poll: kmi_mouse_poll, ++ open: kmi_mouse_open, ++ release: kmi_mouse_release, ++ fasync: kmi_mouse_fasync, ++}; ++ ++static struct miscdevice ps_mouse = { ++ minor: PSMOUSE_MINOR, ++ name: "psaux", ++ fops: &ps_fops, ++}; ++ ++static u_char kmi_mse_init_string[] = { ++ PS2_O_DISABLE, ++ PS2_O_SET_SAMPLE, 100, ++ PS2_O_SET_RES, 3, ++ PS2_O_SET_SCALE21 ++}; ++ ++/* ++ * The "normal" mouse scancode processing ++ */ ++static void kmi_mse_intr(struct kmi_info *kmi, u_int val, struct pt_regs *regs) ++{ ++ u_int head; ++ ++ add_mouse_randomness(val); ++ ++#ifdef CONFIG_AMBA_PS2_RECONNECT ++ /* Try to detect a hot-plug event on the PS/2 mouse port */ ++ switch (kmi->hotplug_state) { ++ case 0: ++ /* Maybe we lost contact... */ ++ if (val == PS2_I_BAT_OK) { ++ kmi->hotplug_state++; ++ DEBUG(("%s: Saw 0xAA. Going to hotplug state %d\n", kmi->name, kmi->hotplug_state)); ++ } ++ break; ++ ++ case 1: ++ /* Again, maybe (but only maybe) we lost contact... */ ++ if (val == 0) { ++ kmi->hotplug_state++; ++ kmi_send(kmi, PS2_O_REQ_STATUS); ++ DEBUG(("%s: Got 0xAA 0x00. Sent Status Request\n", kmi->name)); ++ } else { ++ kmi->hotplug_state = 0; ++ DEBUG(("%s: No 0x00 followed 0xAA. No reconnect.\n", kmi->name)); ++ } ++ break; ++ ++ case 2: ++ /* Eat up acknowledge */ ++ if (val == PS2_I_ACK) ++ kmi->hotplug_state++; ++ else { ++ kmi->hotplug_state = 0; ++ DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val)); ++ } ++ break; ++ ++ case 3: ++ /* check if data reporting is still enabled, then no POR has happend */ ++ kmi->reconnect = !(val & 1<<5); ++ DEBUG(("%s: Data reporting disabled?: (%d)\n", kmi->name, kmi->reconnect)); ++ kmi->hotplug_state++; ++ DEBUG(("%s: Going to hotplug state %d\n", kmi->name, kmi->hotplug_state)); ++ break; ++ ++ case 4: ++ /* Eat up one status byte */ ++ kmi->hotplug_state++; ++ DEBUG(("%s: Going to hotplug state %d\n", kmi->name, kmi->hotplug_state)); ++ break; ++ ++ case 5: ++ /* Eat up another status byte */ ++ if (kmi->reconnect) { ++ kmi->config_num = 0; ++ kmi_send(kmi, kmi_mse_init_string[kmi->config_num]); ++ kmi->config_num++; ++ kmi->hotplug_state++; ++ DEBUG(("%s: Sending byte %d of PS/2 init string.\n", kmi->name, kmi->config_num)); ++ } else { ++ kmi->hotplug_state = 0; ++ DEBUG(("%s: False Alarm...\n", kmi->name)); ++ } ++ break; ++ ++ case 6: ++ if (val == PS2_I_ACK && kmi->config_num < sizeof(kmi_mse_init_string)) { ++ kmi_send(kmi, kmi_mse_init_string[kmi->config_num]); ++ kmi->config_num++; ++ DEBUG(("%s: Sending byte %d of PS/2 init string.\n", kmi->name, kmi->config_num)); ++ } else { ++ if (val == PS2_I_ACK) { ++ DEBUG(("%s: Now enable the mouse again...\n", kmi->name)); ++ queue->head = queue->tail = 0; ++ kmi_send(kmi, PS2_O_ENABLE); ++ kmi->hotplug_state++; ++ } else { ++ kmi->hotplug_state = 0; ++ DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val)); ++ } ++ } ++ break; ++ ++ case 7: ++ /* Eat up last acknowledge from enable */ ++ if (val == PS2_I_ACK) ++ printk(KERN_ERR "%s: reconnected\n", kmi->name); ++ else ++ DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val)); ++ ++ kmi->hotplug_state = 0; ++ break; ++ ++ } /* switch (kmi->hotplug_state) */ ++ ++ /* while inside hotplug mechanism, don't misinterpret values */ ++ if (kmi->hotplug_state > 2) ++ return; ++#endif ++ ++ /* We are waiting for the mouse to respond to a kmi_send_and_wait() */ ++ if (kmi->res == KMI_NO_ACK) { ++ if (val == PS2_I_RESEND) { ++ if (kmi->resend_count < 5) ++ __kmi_send(kmi, kmi->last_tx); ++ else { ++ printk(KERN_ERR "%s: too many resends\n", kmi->name); ++ return; ++ } ++ } ++ ++ if (val == PS2_I_ACK) { ++ kmi->res = val; ++ wake_up(&kmi->wait_q); ++ } ++ return; ++ } ++ ++ /* The mouse autonomously send new data, so wake up mouse_read() */ ++ if (queue) { ++ head = queue->head; ++ queue->buf[head] = val; ++ head = (head + 1) & (BUF_SZ - 1); ++ if (head != queue->tail) { ++ queue->head = head; ++ kill_fasync(&queue->fasync, SIGIO, POLL_IN); ++ wake_up_interruptible(&kmi->wait_q); ++ } ++ } ++} ++ ++static int kmi_init_mouse(struct kmi_info *kmi) ++{ ++ u_int ret, i; ++ ++ if (kmi->present) { ++ kmi->rx = kmi_mse_intr; ++ ++ for (i = 0; i < sizeof(kmi_mse_init_string); i++) { ++ ret = kmi_send_and_wait(kmi, kmi_mse_init_string[i], HZ); ++ if (ret != PS2_I_ACK) ++ printk("%s: didn't get ack (0x%2.2x)\n", ++ kmi->name, ret); ++ } ++ } ++ ++ queue = kmalloc(sizeof(*queue), GFP_KERNEL); ++ if (queue) { ++ memset(queue, 0, sizeof(*queue)); ++ misc_register(&ps_mouse); ++ ret = 0; ++ } else ++ ret = -ENOMEM; ++ ++ return ret; ++} ++#endif /* CONFIG_KMI_MOUSE */ ++ ++/* ++ * The "program" we send to the keyboard to set it up how we want it: ++ * - default typematic delays ++ * - scancode set 1 ++ */ ++static u_char kmi_kbd_init_string[] = { ++ PS2_O_DISABLE, ++ PS2_O_SET_DEFAULT, ++ PS2_O_SET_SCANSET, 0x01, ++ PS2_O_ENABLE ++}; ++ ++static void kmi_kbd_intr(struct kmi_info *kmi, u_int val, struct pt_regs *regs); ++ ++static int __kmi_init_keyboard(struct kmi_info *kmi) ++{ ++ u_int ret, i; ++ ++ if (!kmi->present) ++ return 0; ++ ++ kmi->rx = kmi_kbd_intr; ++ ++ for (i = 0; i < sizeof(kmi_kbd_init_string); i++) { ++ ret = kmi_send_and_wait(kmi, kmi_kbd_init_string[i], HZ); ++ if (ret != PS2_I_ACK) ++ printk("%s: didn't ack (0x%2.2x)\n", ++ kmi->name, ret); ++ } ++ ++ return 0; ++} ++ ++static void kmi_kbd_init_tasklet(unsigned long k) ++{ ++ struct kmi_info *kmi = (struct kmi_info *)k; ++ __kmi_init_keyboard(kmi); ++} ++ ++static DECLARE_TASKLET_DISABLED(kmikbd_init_tasklet, kmi_kbd_init_tasklet, 0); ++ ++/* ++ * The "normal" keyboard scancode processing ++ */ ++static void kmi_kbd_intr(struct kmi_info *kmi, u_int val, struct pt_regs *regs) ++{ ++#ifdef CONFIG_AMBA_PS2_RECONNECT ++ /* Try to detect a hot-plug event on the PS/2 keyboard port */ ++ switch (kmi->hotplug_state) { ++ case 0: ++ /* Maybe we lost contact... */ ++ if (val == PS2_I_BAT_OK) { ++ kmi_send(kmi, PS2_O_SET_SCANSET); ++ kmi->hotplug_state++; ++ DEBUG(("%s: Saw 0xAA. Going to hotplug state %d\n", kmi->name, kmi->hotplug_state)); ++ } ++ break; ++ ++ case 1: ++ /* Eat up acknowledge */ ++ if (val == PS2_I_ACK) { ++ /* Request scan code set: '2' if POR has happend, '1' is false alarm */ ++ kmi_send(kmi, 0); ++ kmi->hotplug_state++; ++ } ++ else { ++ kmi->hotplug_state = 0; ++ DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val)); ++ } ++ break; ++ ++ case 2: ++ /* Eat up acknowledge */ ++ if (val == PS2_I_ACK) ++ kmi->hotplug_state++; ++ else { ++ kmi->hotplug_state = 0; ++ DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val)); ++ } ++ break; ++ ++ case 3: ++ kmi->hotplug_state = 0; ++ if (val == 2) { ++ DEBUG(("%s: POR detected. Scan code is: (%d)\n", kmi->name, val)); ++ kmi->present = 1; ++ tasklet_schedule(&kmikbd_init_tasklet); ++ printk(KERN_ERR "%s: reconnected\n", kmi->name); ++ return; ++ } ++ else ++ DEBUG(("%s: False Alarm...\n", kmi->name)); ++ break; ++ ++ } /* switch (kmi->hotplug_state) */ ++#endif ++ ++ if (val == PS2_I_DIAGFAIL) { ++ printk(KERN_ERR "%s: diagnostic failed\n", kmi->name); ++ return; ++ } ++ ++ /* We are waiting for the keyboard to respond to a kmi_send_and_wait() */ ++ if (kmi->res == KMI_NO_ACK) { ++ if (val == PS2_I_RESEND) { ++ if (kmi->resend_count < 5) ++ __kmi_send(kmi, kmi->last_tx); ++ else { ++ printk(KERN_ERR "%s: too many resends\n", kmi->name); ++ return; ++ } ++ } ++ ++ if (val >= 0xee) { ++ kmi->res = val; ++ wake_up(&kmi->wait_q); ++ } ++ return; ++ } ++ ++#ifdef CONFIG_VT ++ kbd_pt_regs = regs; ++ handle_scancode(val, !(val & 0x80)); ++ tasklet_schedule(&keyboard_tasklet); ++#endif ++} ++ ++static void kmi_intr(int nr, void *devid, struct pt_regs *regs) ++{ ++ struct kmi_info *kmi = devid; ++ u_int status = __raw_readb(KMIIR); ++ ++ if (status & KMIIR_RXINTR) { ++ u_int val = __raw_readb(KMIDATA); ++ ++ if (kmi->rx) ++ kmi->rx(kmi, val, regs); ++ } ++} ++ ++static int kmi_init_keyboard(struct kmi_info *kmi) ++{ ++ kmikbd_init_tasklet.data = (unsigned long)kmi; ++ tasklet_enable(&kmikbd_init_tasklet); ++ ++ return __kmi_init_keyboard(kmi); ++} ++ ++/* ++ * Reset interrupt handler ++ */ ++static void __init ++kmi_reset_intr(struct kmi_info *kmi, u_int val, struct pt_regs *regs) ++{ ++ if (kmi->state == KMI_RESET) { ++ if (val == PS2_I_ACK) ++ kmi->state = KMI_RESET_POR; ++ else { ++ val = KMI_NO_ACK; ++ goto finished; ++ } ++ } else if (kmi->state == KMI_RESET_POR) { ++finished: ++ kmi->res = val; ++ kmi->state = KMI_RESET_DONE; ++ kmi->rx = NULL; ++ wake_up(&kmi->wait_q); ++ } ++} ++ ++/* ++ * Reset the device plugged into this interface ++ */ ++static int __init kmi_reset(struct kmi_info *kmi) ++{ ++ u_int res; ++ int ret = 0; ++ ++ kmi->state = KMI_RESET; ++ kmi->rx = kmi_reset_intr; ++ res = kmi_send_and_wait(kmi, PS2_O_RESET, HZ); ++ kmi->rx = NULL; ++ ++ if (res != PS2_I_BAT_OK) { ++ printk(KERN_ERR "%s: reset failed; ", kmi->name); ++ if (kmi->res != KMI_NO_ACK) ++ printk("code 0x%2.2x\n", kmi->res); ++ else ++ printk("no ack\n"); ++ ret = -EINVAL; ++ } ++ return ret; ++} ++ ++static int __init kmi_init_one_interface(struct kmi_info *kmi) ++{ ++ u_int stat; ++ int ret = -ENODEV; ++ ++ init_waitqueue_head(&kmi->wait_q); ++ ++ printk(KERN_INFO "%s at 0x%8.8x on irq %d (%s)\n", kmi->name, ++ kmi->base, kmi->irq, kmi_type[kmi->type]); ++ ++ /* ++ * Initialise the KMI interface ++ */ ++ __raw_writeb(kmi->divisor, KMICLKDIV); ++ __raw_writeb(KMICR_EN, KMICR); ++ ++ /* ++ * Check that the data and clock lines are OK. ++ */ ++ stat = __raw_readb(KMISTAT); ++ if ((stat & (KMISTAT_IC|KMISTAT_ID)) != (KMISTAT_IC|KMISTAT_ID)) { ++ printk(KERN_ERR "%s: %s%s%sline%s stuck low\n", kmi->name, ++ (stat & KMISTAT_IC) ? "" : "clock ", ++ (stat & (KMISTAT_IC | KMISTAT_ID)) ? "" : "and ", ++ (stat & KMISTAT_ID) ? "" : "data ", ++ (stat & (KMISTAT_IC | KMISTAT_ID)) ? "" : "s"); ++ goto bad; ++ } ++ ++ /* ++ * Claim the appropriate interrupts ++ */ ++ ret = request_irq(kmi->irq, kmi_intr, 0, kmi->name, kmi); ++ if (ret) ++ goto bad; ++ ++ /* ++ * Enable the receive interrupt, and reset the device. ++ */ ++ __raw_writeb(KMICR_EN | KMICR_RXINTREN, KMICR); ++ kmi->present = 1; ++ kmi->present = kmi_reset(kmi) == 0; ++ ++ switch (kmi->type) { ++ case KMI_KEYBOARD: ++ ret = kmi_init_keyboard(kmi); ++ break; ++ ++#ifdef CONFIG_KMI_MOUSE ++ case KMI_MOUSE: ++ ret = kmi_init_mouse(kmi); ++ break; ++#endif ++ } ++ ++ return ret; ++ ++bad: ++ /* ++ * Oh dear, the interface was bad, disable it. ++ */ ++ __raw_writeb(0, KMICR); ++ return ret; ++} ++ ++#ifdef CONFIG_VT ++/* ++ * The fragment between #ifdef above and #endif * CONFIG_VT * ++ * is from the pc_keyb.c driver. It is not copyrighted under the ++ * above notice. This code is by various authors; please see ++ * drivers/char/pc_keyb.c for further information. ++ */ ++ ++/* ++ * Translation of escaped scancodes to keycodes. ++ * This is now user-settable. ++ * The keycodes 1-88,96-111,119 are fairly standard, and ++ * should probably not be changed - changing might confuse X. ++ * X also interprets scancode 0x5d (KEY_Begin). ++ * ++ * For 1-88 keycode equals scancode. ++ */ ++ ++#define E0_KPENTER 96 ++#define E0_RCTRL 97 ++#define E0_KPSLASH 98 ++#define E0_PRSCR 99 ++#define E0_RALT 100 ++#define E0_BREAK 101 /* (control-pause) */ ++#define E0_HOME 102 ++#define E0_UP 103 ++#define E0_PGUP 104 ++#define E0_LEFT 105 ++#define E0_RIGHT 106 ++#define E0_END 107 ++#define E0_DOWN 108 ++#define E0_PGDN 109 ++#define E0_INS 110 ++#define E0_DEL 111 ++ ++#define E1_PAUSE 119 ++ ++/* BTC */ ++#define E0_MACRO 112 ++/* LK450 */ ++#define E0_F13 113 ++#define E0_F14 114 ++#define E0_HELP 115 ++#define E0_DO 116 ++#define E0_F17 117 ++#define E0_KPMINPLUS 118 ++/* ++ * My OmniKey generates e0 4c for the "OMNI" key and the ++ * right alt key does nada. [kkoller@nyx10.cs.du.edu] ++ */ ++#define E0_OK 124 ++/* ++ * New microsoft keyboard is rumoured to have ++ * e0 5b (left window button), e0 5c (right window button), ++ * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] ++ * [or: Windows_L, Windows_R, TaskMan] ++ */ ++#define E0_MSLW 125 ++#define E0_MSRW 126 ++#define E0_MSTM 127 ++ ++static u_char e0_keys[128] = { ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ E0_KPENTER, E0_RCTRL, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, E0_KPSLASH, 0, E0_PRSCR, ++ E0_RALT, 0, 0, 0, ++ 0, E0_F13, E0_F14, E0_HELP, ++ E0_DO, E0_F17, 0, 0, ++ 0, 0, E0_BREAK, E0_HOME, ++ E0_UP, E0_PGUP, 0, E0_LEFT, ++ E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END, ++ E0_DOWN, E0_PGDN, E0_INS, E0_DEL, ++ 0, 0, 0, 0, ++ 0, 0, 0, E0_MSLW, ++ E0_MSRW, E0_MSTM, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, E0_MACRO, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0, ++ 0, 0, 0, 0 ++}; ++ ++#ifdef CONFIG_MAGIC_SYSRQ ++u_char kmi_kbd_sysrq_xlate[128] = ++ "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ ++ "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ ++ "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ ++ "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ ++ "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ ++ "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ ++ "\r\000/"; /* 0x60 - 0x6f */ ++#endif ++ ++int kmi_kbd_setkeycode(u_int scancode, u_int keycode) ++{ ++ if (scancode < 128 || scancode > 255 || keycode > 127) ++ return -EINVAL; ++ e0_keys[scancode - 128] = keycode; ++ return 0; ++} ++ ++int kmi_kbd_getkeycode(u_int scancode) ++{ ++ if (scancode < 128 || scancode > 255) ++ return -EINVAL; ++ return e0_keys[scancode - 128]; ++} ++ ++int kmi_kbd_translate(u_char scancode, u_char *keycode, char raw_mode) ++{ ++ static int prev_scancode = 0; ++ ++ /* special prefix scancodes.. */ ++ if (scancode == 0xe0 || scancode == 0xe1) { ++ prev_scancode = scancode; ++ return 0; ++ } ++ ++ /* 0xff is sent by a few keyboards, ignore it. 0x00 is error */ ++ if (scancode == 0x00 || scancode == 0xff) { ++ prev_scancode = 0; ++ return 0; ++ } ++ ++ scancode &= 0x7f; ++ ++ if (prev_scancode) { ++ int old_scancode = prev_scancode; ++ ++ prev_scancode = 0; ++ switch (old_scancode) { ++ case 0xe0: ++ /* ++ * The keyboard maintains its own internal caps lock ++ * and num lock status. In caps lock mode, E0 AA ++ * precedes make code and E0 2A follows break code. ++ * In numlock mode, E0 2A precedes make code, and ++ * E0 AA follows break code. We do our own book- ++ * keeping, so we will just ignore these. ++ * ++ * For my keyboard there is no caps lock mode, but ++ * there are both Shift-L and Shift-R modes. The ++ * former mode generates E0 2A / E0 AA pairs, the ++ * latter E0 B6 / E0 36 pairs. So, we should also ++ * ignore the latter. - aeb@cwi.nl ++ */ ++ if (scancode == 0x2a || scancode == 0x36) ++ return 0; ++ if (e0_keys[scancode]) ++ *keycode = e0_keys[scancode]; ++ else { ++ if (!raw_mode) ++ printk(KERN_INFO "kbd: unknown " ++ "scancode e0 %02x\n", ++ scancode); ++ return 0; ++ } ++ break; ++ ++ case 0xe1: ++ if (scancode == 0x1d) ++ prev_scancode = 0x100; ++ else { ++ if (!raw_mode) ++ printk(KERN_INFO "kbd: unknown " ++ "scancode e1 %02x\n", ++ scancode); ++ return 0; ++ } ++ break; ++ ++ case 0x100: ++ if (scancode == 0x45) ++ *keycode = E1_PAUSE; ++ else { ++ if (!raw_mode) ++ printk(KERN_INFO "kbd: unknown " ++ "scan code e1 1d %02x\n", ++ scancode); ++ return 0; ++ } ++ break; ++ } ++ } else ++ *keycode = scancode; ++ return 1; ++} ++ ++char kmi_kbd_unexpected_up(u_char keycode) ++{ ++ return 0x80; ++} ++ ++void kmi_kbd_leds(u_char leds) ++{ ++ struct kmi_info *kmi = kmi_keyb; ++ u_int ret; ++ ++ if (kmi) { ++ ret = kmi_send_and_wait(kmi, PS2_O_INDICATORS, HZ); ++ if (ret != KMI_NO_ACK) ++ ret = kmi_send_and_wait(kmi, leds, HZ); ++ if (ret == KMI_NO_ACK) ++ kmi->present = 0; ++ } ++} ++ ++int __init kmi_kbd_init(void) ++{ ++ int ret = -ENODEV; ++ ++ if (kmi_keyb) { ++ strcpy(kmi_keyb->name, "kmikbd"); ++ ret = kmi_init_one_interface(kmi_keyb); ++ } ++ ++ if (ret == 0) { ++ k_setkeycode = kmi_kbd_setkeycode; ++ k_getkeycode = kmi_kbd_getkeycode; ++ k_translate = kmi_kbd_translate; ++ k_unexpected_up = kmi_kbd_unexpected_up; ++ k_leds = kmi_kbd_leds; ++#ifdef CONFIG_MAGIC_SYSRQ ++ k_sysrq_xlate = kmi_kbd_sysrq_xlate; ++ k_sysrq_key = 0x54; ++#endif ++ } ++ ++ return ret; ++} ++ ++#endif /* CONFIG_VT */ ++ ++int register_kmi(struct kmi_info *kmi) ++{ ++ struct kmi_info **kmip = NULL; ++ int ret; ++ ++ if (kmi->type == KMI_KEYBOARD) ++ kmip = &kmi_keyb; ++ else if (kmi->type == KMI_MOUSE) ++ kmip = &kmi_mouse; ++ ++ ret = -EINVAL; ++ if (kmip) { ++ ret = -EBUSY; ++ if (!*kmip) { ++ *kmip = kmi; ++ ret = 0; ++ } ++ } ++ ++ return ret; ++} ++ ++#ifdef CONFIG_KMI_MOUSE ++static int __init kmi_init(void) ++{ ++ int ret = -ENODEV; ++ ++ if (kmi_mouse) { ++ strcpy(kmi_mouse->name, "kmimouse"); ++ ret = kmi_init_one_interface(kmi_mouse); ++ } ++ ++ return ret; ++} ++ ++__initcall(kmi_init); ++#endif +diff -urN linux-2.4.26/drivers/char/anakin_ts.c linux-2.4.26-vrs1/drivers/char/anakin_ts.c +--- linux-2.4.26/drivers/char/anakin_ts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/anakin_ts.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,208 @@ ++/* ++ * linux/drivers/char/anakin_ts.c ++ * ++ * Copyright (C) 2001 Aleph One Ltd. for Acunia N.V. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Changelog: ++ * 18-Apr-2001 TTC Created ++ * 23-Oct-2001 dwmw2 Cleanup ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* ++ * TSBUF_SIZE must be a power of two ++ */ ++#define ANAKIN_TS_MINOR 16 ++#define TSBUF_SIZE 256 ++#define NEXT(index) (((index) + 1) & (TSBUF_SIZE - 1)) ++ ++static unsigned short buffer[TSBUF_SIZE][4]; ++static int head, tail; ++static DECLARE_WAIT_QUEUE_HEAD(queue); ++static DECLARE_MUTEX(open_sem); ++static spinlock_t tailptr_lock = SPIN_LOCK_UNLOCKED; ++static struct fasync_struct *fasync; ++ ++/* ++ * Interrupt handler and standard file operations ++ */ ++static void ++anakin_ts_handler(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ unsigned int status = __raw_readl(IO_BASE + IO_CONTROLLER + 0x24); ++ ++ /* ++ * iPAQ format (u16 pressure, x, y, millisecs) ++ */ ++ switch (status >> 20 & 3) { ++ case 0: ++ return; ++ case 2: ++ buffer[head][0] = 0; ++ break; ++ default: ++ buffer[head][0] = 0x7f; ++ } ++ ++ if (unlikely((volatile int)tail == NEXT(head))) { ++ /* Run out of space in the buffer. Move the tail pointer */ ++ spin_lock(&tailptr_lock); ++ ++ if ((volatile int)tail == NEXT(head)) { ++ tail = NEXT(NEXT(head)); ++ } ++ spin_unlock(&tailptr_lock); ++ } ++ ++ buffer[head][1] = status >> 2 & 0xff; ++ buffer[head][2] = status >> 12 & 0xff; ++ buffer[head][3] = jiffies; ++ mb(); ++ head = NEXT(head); ++ ++ wake_up_interruptible(&queue); ++ kill_fasync(&fasync, SIGIO, POLL_IN); ++ ++} ++ ++static ssize_t ++anakin_ts_read(struct file *filp, char *buf, size_t count, loff_t *l) ++{ ++ unsigned short data[4]; ++ ssize_t written = 0; ++ ++ if (head == tail) { ++ if (filp->f_flags & O_NONBLOCK) ++ return -EAGAIN; ++ if (wait_event_interruptible(queue, (volatile int)head != (volatile int)tail)) ++ return -ERESTARTSYS; ++ } ++ ++ while ((volatile int)head != (volatile int)tail && count >= sizeof data) { ++ /* Copy the data out with the spinlock held, so the ++ interrupt can't fill the buffer and move the tail ++ pointer while we're doing it */ ++ spin_lock_irq(&tailptr_lock); ++ ++ memcpy(data, buffer[tail], sizeof data); ++ tail = NEXT(tail); ++ ++ spin_unlock_irq(&tailptr_lock); ++ ++ if (copy_to_user(buf, data, sizeof data)) ++ return -EFAULT; ++ count -= sizeof data; ++ buf += sizeof data; ++ written += sizeof data; ++ } ++ return written ? written : -EINVAL; ++} ++ ++static unsigned int ++anakin_ts_poll(struct file *filp, poll_table *wait) ++{ ++ poll_wait(filp, &queue, wait); ++ return head != tail ? POLLIN | POLLRDNORM : 0; ++} ++ ++static int ++anakin_ts_ioctl(struct inode *inode, struct file *filp, ++ unsigned int cmd, unsigned long arg) ++{ ++ /* ++ * Future ioctl goes here ++ */ ++ return 0; ++} ++ ++static int ++anakin_ts_open(struct inode *inode, struct file *filp) ++{ ++ if (down_trylock(&open_sem)) ++ return -EBUSY; ++ return 0; ++} ++ ++static int ++anakin_ts_fasync(int fd, struct file *filp, int on) ++{ ++ return fasync_helper(fd, filp, on, &fasync); ++} ++ ++static int ++anakin_ts_release(struct inode *inode, struct file *filp) ++{ ++ anakin_ts_fasync(-1, filp, 0); ++ up(&open_sem); ++ return 0; ++} ++ ++static struct file_operations anakin_ts_fops = { ++ owner: THIS_MODULE, ++ read: anakin_ts_read, ++ poll: anakin_ts_poll, ++ ioctl: anakin_ts_ioctl, ++ open: anakin_ts_open, ++ release: anakin_ts_release, ++ fasync: anakin_ts_fasync, ++}; ++ ++static struct miscdevice anakin_ts_miscdev = { ++ ANAKIN_TS_MINOR, ++ "anakin_ts", ++ &anakin_ts_fops ++}; ++ ++/* ++ * Initialization and exit routines ++ */ ++int __init ++anakin_ts_init(void) ++{ ++ int retval; ++ ++ if ((retval = request_irq(IRQ_TOUCHSCREEN, anakin_ts_handler, ++ SA_INTERRUPT, "anakin_ts", 0))) { ++ printk(KERN_WARNING "anakin_ts: failed to get IRQ\n"); ++ return retval; ++ } ++ __raw_writel(1, IO_BASE + IO_CONTROLLER + 8); ++ misc_register(&anakin_ts_miscdev); ++ ++ printk(KERN_NOTICE "Anakin touchscreen driver initialised\n"); ++ ++ return 0; ++} ++ ++void __exit ++anakin_ts_exit(void) ++{ ++ __raw_writel(0, IO_BASE + IO_CONTROLLER + 8); ++ free_irq(IRQ_TOUCHSCREEN, 0); ++ misc_deregister(&anakin_ts_miscdev); ++} ++ ++module_init(anakin_ts_init); ++module_exit(anakin_ts_exit); ++ ++MODULE_AUTHOR("Tak-Shing Chan "); ++MODULE_DESCRIPTION("Anakin touchscreen driver"); ++MODULE_SUPPORTED_DEVICE("touchscreen/anakin"); +diff -urN linux-2.4.26/drivers/char/cerf_keyb.c linux-2.4.26-vrs1/drivers/char/cerf_keyb.c +--- linux-2.4.26/drivers/char/cerf_keyb.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/cerf_keyb.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,380 @@ ++/* ++ cerf_keyb.c: This is the end. Daniel is writing a device driver!!! ++*/ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define KBD_REPORT_UNKN ++ ++#define KBD_REPORT_ERR /* Report keyboard errors */ ++#define KBD_REPORT_UNKN /* Report unknown scan codes */ ++#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */ ++#define KBD_NO_DATA (-1) /* No data */ ++#define KBD_REPEAT_START (0x20) ++#define KBD_REPEAT_CONTINUE (0x05) ++#define KBD_KEY_DOWN_MAX (0x10) ++#define UINT_LEN (20) ++#define SC_LIM (69) ++#define KBD_ROWS (5) ++#define KBD_COLUMNS (8) ++ ++#define KBD_KEYUP (0x80) ++#define KBD_MODESCAN (0x7f) ++#define KBD_CAPSSCAN (0x3a) ++#define KBD_SHIFTSCAN (0x2a) ++#define KBD_NUMCURSCAN (0x7c) ++#define KBD_CTRLSCAN (0x1d) ++#define KBD_ALTSCAN (0x38) ++ ++#define KBD_UP_OFF (0) ++#define KBD_UP_ON (1) ++#define KBD_DOWN (2) ++#define KBD_DOWN_HOLD (3) ++ ++ ++ ++static unsigned char handle_kbd_event(void); ++static unsigned char kbd_read_input(void); ++static void column_set(unsigned int column); ++static int scancodes(unsigned char codeval[KBD_ROWS][KBD_COLUMNS]); ++ ++static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; ++static struct timer_list kbd_timer; ++ ++static short mode_ena = 0; ++static short numcur_ena = 0; ++static short shift_ena = 0; ++ ++#define E0_KPENTER 96 ++#define E0_RCTRL 97 ++#define E0_KPSLASH 98 ++#define E0_PRSCR 99 ++#define E0_RALT 100 ++#define E0_BREAK 101 /* (control-pause) */ ++#define E0_HOME 102 ++#define E0_UP 103 ++#define E0_PGUP 104 ++#define E0_LEFT 105 ++#define E0_RIGHT 106 ++#define E0_END 107 ++#define E0_DOWN 108 ++#define E0_PGDN 109 ++#define E0_INS 110 ++#define E0_DEL 111 ++#define E1_PAUSE 119 ++#define E0_MACRO 112 ++#define E0_F13 113 ++#define E0_F14 114 ++#define E0_HELP 115 ++#define E0_DO 116 ++#define E0_F17 117 ++#define E0_KPMINPLUS 118 ++#define E0_OK 124 ++#define E0_MSLW 125 ++#define E0_MSRW 126 ++#define E0_MSTM 127 ++ ++static unsigned char e0_keys[128] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ ++ 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ ++ 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ ++ E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ ++ E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ ++ E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ ++ E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ ++ 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ ++ 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ ++ 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ ++}; ++ ++static unsigned char cerf_normal_map[KBD_ROWS][KBD_COLUMNS] = { ++ {KBD_ALTSCAN, KBD_MODESCAN, 0x1e, 0x30, 0x2e, 0x20, 0x00, 0x00}, ++ {0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x00}, ++ {0x26, 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x00}, ++ {0x1f, 0x14, 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x00}, ++ {0x2c, KBD_SHIFTSCAN, KBD_CTRLSCAN, 0x39, KBD_NUMCURSCAN, 0x2b, 0x1c, 0x00} ++}; ++ ++static unsigned char cerf_mode_map[KBD_ROWS][KBD_COLUMNS] = { ++ {0x00, 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00}, ++ {0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x00}, // ++ {0x0d, 0x0c, 0x37, 0x35, 0x0d, 0x48, 0x28, 0x00}, ++ {0x01, 0x33, 0x34, 0x00, 0x4b, 0x27, 0x4d, 0x00}, // ++ {0x0f, 0x00, KBD_CAPSSCAN, 0x0e, 0x00, 0x50, 0x00, 0x00} ++}; ++ ++static unsigned char cerf_numcur_map[KBD_ROWS][KBD_COLUMNS] = { ++ {0x00, 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00}, ++ {0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x00}, ++ {0x0d, 0x0c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00}, ++ {0x00, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x4d, 0x00}, ++ {0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00} ++}; ++ ++static void column_set(unsigned int column) ++{ ++ if (column < 0) ++ { ++ CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_A, 0xFF, 0xFF); ++ CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_B, 0xFF, 0xFF); ++ } ++ else ++ { ++ if(column < 4) ++ { ++ CERF_PDA_CPLD_Set(CERF_PDA_CPLD_KEYPAD_A, 1 << (column % 4), 0xFF); ++ CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_B, 0xFF, 0xFF); ++ } ++ else ++ { ++ CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_A, 0xFF, 0xFF); ++ CERF_PDA_CPLD_Set(CERF_PDA_CPLD_KEYPAD_B, 1 << (column % 4), 0xFF); ++ } ++ } ++} ++ ++static int scancodes(unsigned char codeval[KBD_ROWS][KBD_COLUMNS]) ++{ ++ int i, j; ++ ++ for(i = 0; i < KBD_COLUMNS; i++) ++ { ++ column_set(i); ++ udelay(50); ++ for(j = 0; j < KBD_ROWS; j++) ++ { ++ if(mode_ena) ++ codeval[j][i] = (GPLR & (1 << (20 + j)))?(cerf_mode_map[j][i]?cerf_mode_map[j][i]:cerf_normal_map[j][i]):0; ++ else if(numcur_ena) ++ codeval[j][i] = (GPLR & (1 << (20 + j)))?(cerf_numcur_map[j][i]?cerf_numcur_map[j][i]:cerf_normal_map[j][i]):0; ++ else ++ codeval[j][i] = (GPLR & (1 << (20 + j)))?cerf_normal_map[j][i]:0; ++ } ++ } ++ column_set(-1); ++ ++ return 0; ++} ++ ++static unsigned char kbd_read_input(void) ++{ ++ int i, j, k, l; ++ unsigned char prev; ++ static unsigned char count = 0; ++ ++ static unsigned char oldcodes[KBD_ROWS][KBD_COLUMNS]={{0,0,0,0,0,0,0,0}, ++ {0,0,0,0,0,0,0,0}, ++ {0,0,0,0,0,0,0,0}, ++ {0,0,0,0,0,0,0,0}, ++ {0,0,0,0,0,0,0,0}}; ++ unsigned char inputcode[KBD_ROWS][KBD_COLUMNS]; ++ ++ memset(inputcode, 0, sizeof(unsigned char) * (KBD_ROWS * KBD_COLUMNS)); ++ scancodes(inputcode); ++ ++ for(i = 0; i < KBD_COLUMNS; i++) ++ { ++ for(j = 0; j < KBD_ROWS; j++) ++ { ++// if(oldcodes[j][i] == 0xe0) ++// oldcodes[j][i] = ++ if(oldcodes[j][i] != inputcode[j][i]) ++ { ++ // Value of the key before entering this function ++ prev = oldcodes[j][i]; ++ ++ // KEYUP ++ if(inputcode[j][i] == 0 && oldcodes[j][i] != 0 && !(oldcodes[j][i] & KBD_KEYUP)) ++ { ++ oldcodes[j][i] |= KBD_KEYUP; ++ ++ if(mode_ena == KBD_UP_ON) ++ mode_ena = KBD_UP_OFF; ++ if(prev == KBD_MODESCAN) ++ if(mode_ena == KBD_DOWN_HOLD) ++ mode_ena = KBD_UP_OFF; ++ else if(mode_ena == KBD_DOWN) ++ mode_ena = KBD_UP_ON; ++ if(mode_ena == KBD_DOWN) ++ mode_ena = KBD_DOWN_HOLD; ++ } ++ // RESET KEYUP ++ else if(oldcodes[j][i] & KBD_KEYUP) ++ oldcodes[j][i] = 0; ++ // KEY DOWN ++ else ++ { ++ oldcodes[j][i] = inputcode[j][i]; ++ ++ // Parse out mode modifiers before the keyboard interpreter can touch them ++ if(inputcode[j][i] == KBD_MODESCAN) ++ { ++ if(!mode_ena) ++ mode_ena = KBD_DOWN; ++ continue; ++ } ++ if(inputcode[j][i] == KBD_NUMCURSCAN) ++ { ++ numcur_ena = numcur_ena?0:1; ++ continue; ++ } ++ } ++ //printk("Modified: (%#x,%#x), ipv:%#x, To: (%#.2x), From: (%#.2x), Flags:%d,%d,%d\r\n", j, i, inputcode[j][i], oldcodes[j][i], prev, mode_ena, shift_ena, numcur_ena); ++ return oldcodes[j][i]; ++ } ++ } ++ } ++ ++ return (unsigned char)(KBD_NO_DATA); ++} ++ ++int cerf_kbd_translate(unsigned char scancode, unsigned char *keycode, ++ char raw_mode) ++{ ++ static int prev_scancode; ++ ++ if (scancode == 0xe0 || scancode == 0xe1) { ++ prev_scancode = scancode; ++ return 0; ++ } ++ ++ if (scancode == 0x00 || scancode == 0xff) { ++ prev_scancode = 0; ++ return 0; ++ } ++ ++ scancode &= 0x7f; ++ ++ if (prev_scancode) { ++ if (prev_scancode != 0xe0) { ++ if (prev_scancode == 0xe1 && scancode == 0x1d) { ++ prev_scancode = 0x100; ++ return 0; ++ } else if (prev_scancode == 0x100 && scancode == 0x45) { ++ prev_scancode = 0; ++ } else { ++#ifdef KBD_REPORT_UNKN ++ if (!raw_mode) ++ printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); ++#endif ++ prev_scancode = 0; ++ return 0; ++ } ++ } else { ++ prev_scancode = 0; ++ if (scancode == 0x2a || scancode == 0x36) ++ return 0; ++ else { ++#ifdef KBD_REPORT_UNKN ++ if (!raw_mode) ++ printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", ++ scancode); ++#endif ++ return 0; ++ } ++ } ++ } else ++ *keycode = scancode; ++ return 1; ++} ++ ++static inline void handle_keyboard_event(unsigned char scancode) ++{ ++ if(scancode != (unsigned char)(KBD_NO_DATA)) ++ { ++#ifdef CONFIG_VT ++ handle_scancode(scancode, !(scancode & KBD_KEYUP)); ++#endif ++ tasklet_schedule(&keyboard_tasklet); ++ } ++} ++ ++static unsigned char handle_kbd_event(void) ++{ ++ unsigned char scancode; ++ ++ scancode = kbd_read_input(); ++ handle_keyboard_event(scancode); ++ ++ return 0; ++} ++ ++/* Handle the automatic interrupts handled by the timer */ ++static void keyboard_interrupt(unsigned long foo) ++{ ++ spin_lock_irq(&kbd_controller_lock); ++ handle_kbd_event(); ++ spin_unlock_irq(&kbd_controller_lock); ++ ++ kbd_timer.expires = 8 + jiffies; ++ kbd_timer.data = 0x00000000; ++ kbd_timer.function = (void(*)(unsigned long))&keyboard_interrupt; ++ ++ add_timer(&kbd_timer); ++} ++ ++void cerf_leds(unsigned char leds) ++{ ++} ++char cerf_unexpected_up(unsigned char keycode) ++{ ++return 0; ++} ++int cerf_getkeycode(unsigned int scancode) ++{ ++return 0; ++} ++int cerf_setkeycode(unsigned int scancode, unsigned int keycode) ++{ ++return 0; ++} ++ ++void cerf_kbd_init_hw(void) ++{ ++ printk("Starting Cerf PDA Keyboard Driver... "); ++ ++ k_setkeycode = cerf_setkeycode; ++ k_getkeycode = cerf_getkeycode; ++ k_translate = cerf_kbd_translate; ++ k_unexpected_up = cerf_unexpected_up; ++ k_leds = cerf_leds; ++ ++ GPDR &= ~(GPIO_GPIO(20) | GPIO_GPIO(21) | GPIO_GPIO(22) | GPIO_GPIO(23) | GPIO_GPIO(24)); ++ kbd_timer.expires = 40 + jiffies; ++ kbd_timer.data = 0x00000000; ++ kbd_timer.function = (void(*)(unsigned long))&keyboard_interrupt; ++ ++ add_timer(&kbd_timer); ++ ++ printk("Done\r\n"); ++} +diff -urN linux-2.4.26/drivers/char/clps711x_keyb.c linux-2.4.26-vrs1/drivers/char/clps711x_keyb.c +--- linux-2.4.26/drivers/char/clps711x_keyb.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/clps711x_keyb.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,547 @@ ++/* ++ * drivers/char/clps711x_keyb.c ++ * ++ * Copyright (C) 2001 Thomas Gleixner ++ * ++ * based on drivers/edb7211_keyb.c, which is copyright (C) 2000 Bluemug Inc. ++ * ++ * Keyboard driver for ARM Linux on EP7xxx and CS89712 processors ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. See the file COPYING ++ * in the main directory of this archive for more details. ++ * ++ * ++ * Hardware: ++ * ++ * matrix scan keyboards based on EP7209,7211,7212,7312 and CS89712 ++ * on chip keyboard scanner. ++ * Adaption for different machines is done in init function. ++ * ++ * Basic Function: ++ * ++ * Basicly the driver is interrupt driven. It sets all column drivers ++ * high. If any key is pressed, a interrupt occures. Now a seperate scan of ++ * each column is done. This scan is timer based, because we use a keyboard ++ * interface with decoupling capacitors (neccecary if you want to survive ++ * EMC compliance tests). Always one line is set high. When next timer event ++ * occures the scan data on port A are valid. This makes also sure, that no ++ * spurious keys are scanned. The kbd int on these CPU's is not deglitched! ++ * After scanning all columns, we switch back to int mode, if no key is ++ * pressed. If any is pressed we reschedule the scan within a programmable ++ * delay. If we would switch back to interrupt mode as long as a key is pressed, ++ * we come right back to the interrupt, because the int. is level triggered ! ++ * The timer based scan of the seperate columns can also be done in one ++ * timer event (set fastscan to 1). ++ * ++ * Summary: ++ * The design of this keyboard controller chip is stupid at all ! ++ * ++ * Matrix translation: ++ * The matrix translation table is based on standard XT scancodes. Maybe ++ * you have to adjust the KEYISPRINTABLE macro if you set other codes. ++ * ++ * HandyKey: ++ * ++ * On small matrix keyboards you don't have enough keys for operation. ++ * The intention was to implement a operation mode as it's used on handys. ++ * You can rotate trough four scancode levels and produce e.g. with a 4x3 ++ * matrix 4*3*4 = 48 different keycodes. That's basicly enough for editing ++ * filenames or things like that. The HandyKey function takes care about ++ * nonprintable keys like cursors, backspace, del ... ++ * If a key is pressed and is a printable keycode, the code is put to the ++ * main keyboard handler and a cursor left is applied. If you press the same ++ * key again, the current character is deleted and the next level character ++ * is applied. (e.g. 1, a, b, c, 1 ....). If you press a different key, the ++ * driver applies cursor right, before processing the new key. ++ * The autocomplete feature moves the cursor right, if you do not press a ++ * key within a programmable time. ++ * If HandyKey is off, the keyboard behaviour is that of a standard keyboard ++ * HandyKey can be en/disabled from userspace with the proc/keyboard entry ++ * ++ * proc/keyboard: ++ * ++ * Read access gives back the actual state of the HandyKey function ++ * h:0 Disabled ++ * h:1 Enabled ++ * Write access has two functions. Changing the HandyKey mode and applying ++ * a different scancode translation table. ++ * Syntax is: h:0 disable Handykey ++ * h:1 enabled Handykey ++ * t:array[256] of bytes Transfer translation table ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++void clps711x_kbd_init_hw(void); ++ ++/* ++ * Values for the keyboard column scan control register. ++ */ ++#define KBSC_HI 0x0 /* All driven high */ ++#define KBSC_LO 0x1 /* All driven low */ ++#define KBSC_X 0x2 /* All high impedance */ ++#define KBSC_COL0 0x8 /* Column 0 high, others high impedance */ ++#define KBSC_COL1 0x9 /* Column 1 high, others high impedance */ ++#define KBSC_COL2 0xa /* Column 2 high, others high impedance */ ++#define KBSC_COL3 0xb /* Column 3 high, others high impedance */ ++#define KBSC_COL4 0xc /* Column 4 high, others high impedance */ ++#define KBSC_COL5 0xd /* Column 5 high, others high impedance */ ++#define KBSC_COL6 0xe /* Column 6 high, others high impedance */ ++#define KBSC_COL7 0xf /* Column 7 high, others high impedance */ ++ ++/* ++* Keycodes for cursor left/right and delete (used by HandyKey) ++*/ ++#define KEYCODE_CLEFT 0x4b ++#define KEYCODE_CRIGHT 0x4d ++#define KEYCODE_DEL 0x53 ++#define KEYISPRINTABLE(code) ( (code > 0x01 && code < 0x37 && code != 0x1c \ ++ && code != 0x0e) || code == 0x39) ++ ++/* Simple translation table for the SysRq keys */ ++#ifdef CONFIG_MAGIC_SYSRQ ++unsigned char clps711x_kbd_sysrq_xlate[128] = ++ "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ ++ "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ ++ "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ ++ "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ ++ "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ ++ "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ ++ "\r\000/"; /* 0x60 - 0x6f */ ++#endif ++ ++/* ++ * This table maps row/column keyboard matrix positions to XT scancodes. ++ * It's a default table, which can be overriden by writing to proc/keyboard ++ */ ++#ifdef CONFIG_ARCH_AUTCPU12 ++static unsigned char autcpu12_scancode[256] = ++{ ++/* Column: ++ Row 0 1 2 3 4 5 6 7 */ ++/* A0 */ 0x08, 0x09, 0x0a, 0x0e, 0x05, 0x06, 0x00, 0x00, ++/* A1 */ 0x07, 0x53, 0x02, 0x03, 0x04, 0x0f, 0x00, 0x00, ++/* A2 */ 0x0c, 0x0b, 0x33, 0x1c, 0xff, 0x4b, 0x00, 0x00, ++/* A3 */ 0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00, ++/* A4 */ 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* A5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* A6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* A7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ ++/* A0 */ 0x1e, 0x20, 0x22, 0x0e, 0x24, 0x32, 0x00, 0x00, ++/* A1 */ 0x19, 0x53, 0x1f, 0x2f, 0x15, 0x0f, 0x00, 0x00, ++/* A2 */ 0x0c, 0x39, 0x34, 0x1c, 0xff, 0x4b, 0x00, 0x00, ++/* A3 */ 0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00, ++/* A4 */ 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* A5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* A6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* A7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ ++/* A0 */ 0x30, 0x12, 0x23, 0x0e, 0x25, 0x31, 0x00, 0x00, ++/* A1 */ 0x10, 0x53, 0x14, 0x11, 0x2c, 0x0f, 0x00, 0x00, ++/* A2 */ 0x0c, 0x0b, 0x27, 0x1c, 0xff, 0x4b, 0x00, 0x00, ++/* A3 */ 0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00, ++/* A4 */ 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* A5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* A6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* A7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ ++/* A0 */ 0x2e, 0x21, 0x17, 0x0e, 0x26, 0x18, 0x00, 0x00, ++/* A1 */ 0x13, 0x53, 0x16, 0x2D, 0x04, 0x0f, 0x00, 0x00, ++/* A2 */ 0x0c, 0x39, 0x35, 0x1c, 0xff, 0x4b, 0x00, 0x00, ++/* A3 */ 0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00, ++/* A4 */ 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* A5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* A6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* A7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++}; ++#endif ++ ++static int keys[8]; ++static int new_keys[8]; ++static int previous_keys[8]; ++ ++static int fastscan; ++static int scan_interval; ++static int scan_delay; ++static int last_column; ++static int key_is_pressed; ++ ++static unsigned char *act_scancode; ++ ++static struct kbd_handy_key { ++ int ena; ++ int code; ++ int shift; ++ int autocomplete; ++ unsigned long expires; ++ unsigned long delay; ++ unsigned char left; ++ unsigned char right; ++ unsigned char del; ++} khandy; ++ ++static struct tq_struct kbd_process_task; ++static struct timer_list clps711x_kbd_timer; ++static struct timer_list clps711x_kbdhandy_timer; ++static struct proc_dir_entry *clps711x_keyboard_proc_entry = NULL; ++ ++/* ++ * Translate a raw keycode to an XT keyboard scancode. ++ */ ++static int clps711x_translate(unsigned char scancode, unsigned char *keycode, ++ char raw_mode) ++{ ++ *keycode = act_scancode[scancode]; ++ return 1; ++} ++ ++/* ++* Initialize handykey structure ++* clear code, clear shift ++* scan scancode for cursor right/left and delete ++*/ ++static void clps711x_handykey_init(void) { ++ ++ int i; ++ ++ khandy.ena = 0; ++ khandy.code = 0; ++ khandy.shift = 0; ++ khandy.autocomplete = 0; ++ for(i = 0; i < 64; i++) { ++ switch(act_scancode[i]) { ++ case KEYCODE_CLEFT: khandy.left = i; break; ++ case KEYCODE_CRIGHT: khandy.right = i; break; ++ case KEYCODE_DEL: khandy.del = i; break; ++ } ++ } ++} ++ ++/* ++* Check for handy key and process it ++*/ ++void inline clps711x_checkhandy(int col, int row) { ++ ++ int scode, down; ++ unsigned char kcode; ++ ++ scode = (row<<3) + col; ++ down = keys[col]>>row & 0x01; ++ kcode = act_scancode[scode]; ++ ++ if (!khandy.ena) { ++ if (khandy.code) { ++ handle_scancode(khandy.right,1); ++ handle_scancode(khandy.right,0); ++ } ++ khandy.code = 0; ++ khandy.shift = 0; ++ khandy.autocomplete = 0; ++ } ++ ++ if(!kcode) ++ return; ++ ++ if (!down || !khandy.ena) { ++ if (khandy.ena && KEYISPRINTABLE(act_scancode[scode])) ++ khandy.autocomplete = 1; ++ else ++ handle_scancode(scode + khandy.shift, down); ++ return; ++ } ++ ++ khandy.autocomplete = 0; ++ if (KEYISPRINTABLE(kcode)) { ++ if (khandy.code) { ++ if(khandy.code == (scode|0x100)) { ++ handle_scancode(khandy.del,1); ++ handle_scancode(khandy.del,0); ++ khandy.shift = khandy.shift < 3*64 ? khandy.shift + 64 : 0 ; ++ } else { ++ handle_scancode(khandy.right,1); ++ handle_scancode(khandy.right,0); ++ khandy.shift = 0; ++ } ++ } ++ handle_scancode(scode + khandy.shift, 1); ++ handle_scancode(scode + khandy.shift, 0); ++ khandy.code = scode | 0x100; ++ handle_scancode(khandy.left,1); ++ handle_scancode(khandy.left,0); ++ } else { ++ if (khandy.code) { ++ khandy.code = 0; ++ handle_scancode(khandy.right,1); ++ handle_scancode(khandy.right,0); ++ } ++ khandy.shift = 0; ++ handle_scancode(scode, down); ++ } ++} ++ ++ ++/* ++ * Process the new key data ++ */ ++static void clps711x_kbd_process(void* data) ++{ ++ int col,row,res; ++ ++ for (col = 0; col < 8; col++) { ++ if (( res = previous_keys[col] ^ keys[col]) == 0) ++ continue; ++ for(row = 0; row < 8; row++) { ++ if ( ((res >> row) & 0x01) != 0) ++ clps711x_checkhandy(col,row); ++ } ++ } ++ /* Update the state variables. */ ++ memcpy(previous_keys, keys, 8 * sizeof(int)); ++ ++ /* reschedule, if autocomplete pending */ ++ if (khandy.autocomplete) { ++ khandy.expires = jiffies + khandy.delay; ++ mod_timer(&clps711x_kbdhandy_timer,khandy.expires); ++ } ++ ++} ++ ++static char clps711x_unexpected_up(unsigned char scancode) ++{ ++ return 0200; ++} ++ ++/* ++* Handle timer event, for autocomplete function ++* Reschedule keyboard process task ++*/ ++static void clps711x_kbdhandy_timeout(unsigned long data) ++{ ++ if(khandy.autocomplete) { ++ khandy.code = 0; ++ khandy.shift = 0; ++ khandy.autocomplete = 0; ++ handle_scancode(khandy.right,1); ++ handle_scancode(khandy.right,0); ++ } ++} ++ ++/* ++* Handle timer event, while in pollmode ++*/ ++static void clps711x_kbd_timeout(unsigned long data) ++{ ++ int i; ++ unsigned long flags; ++ /* ++ * read bits of actual column or all columns in fastscan-mode ++ */ ++ for (i = 0; i < 8; i++) { ++ new_keys[last_column - KBSC_COL0] = clps_readb(PADR) & 0xff; ++ key_is_pressed |= new_keys[last_column - KBSC_COL0]; ++ last_column = last_column < KBSC_COL7 ? last_column + 1 : KBSC_COL0; ++ local_irq_save(flags); ++ clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) ++ | last_column, SYSCON1); ++ local_irq_restore(flags); ++ /* ++ * For fastscan, apply a short delay to settle scanlines ++ * else break and wait for next timeout ++ */ ++ if (fastscan) ++ udelay(5); ++ else ++ break; ++ } ++ ++ if (key_is_pressed) ++ khandy.autocomplete = 0; ++ ++ /* ++ * switch to interupt mode, if all columns scanned and no key pressed ++ * else reschedule scan ++ */ ++ if (last_column == KBSC_COL0) { ++ if (!key_is_pressed) { ++ local_irq_save(flags); ++ clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) ++ | KBSC_HI, SYSCON1); ++ local_irq_restore(flags); ++ clps_writel(0,KBDEOI); ++ enable_irq(IRQ_KBDINT); ++ } else { ++ clps711x_kbd_timer.expires = jiffies + scan_interval; ++ add_timer(&clps711x_kbd_timer); ++ } ++ key_is_pressed = 0; ++ memcpy(keys, new_keys, 8 * sizeof(int)); ++ for (i = 0; i < 8; i++) { ++ if (previous_keys[i] != keys[i]) { ++ queue_task(&kbd_process_task, &tq_timer); ++ return; ++ } ++ } ++ } else { ++ clps711x_kbd_timer.expires = jiffies + scan_delay; ++ add_timer(&clps711x_kbd_timer); ++ } ++} ++ ++/* ++* Keyboard interrupt, change to scheduling mode ++*/ ++static void clps711x_kbd_int(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ ++#ifdef CONFIG_VT ++ kbd_pt_regs = regs; ++#endif ++ disable_irq(IRQ_KBDINT); ++ khandy.autocomplete = 0; ++ clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) ++ | KBSC_COL0, SYSCON1); ++ clps711x_kbd_timer.expires = jiffies + scan_delay; ++ add_timer(&clps711x_kbd_timer); ++} ++ ++ ++static int clps711x_kbd_proc_keyboard_read(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ if (count < 2) ++ return -EINVAL; ++ ++ return sprintf(page,"h:%d\n",khandy.ena); ++} ++ ++static int clps711x_kbd_proc_keyboard_write(struct file *file, const char *buffer, ++ unsigned long count, void *data) ++{ ++ unsigned char buf[260]; ++ ++ if (count < 3|| count > 258) ++ return -EINVAL; ++ if (copy_from_user(buf, buffer, count)) ++ return -EFAULT; ++ if (buf[1] != ':') ++ return -EINVAL; ++ ++ if (buf[0] == 'h') { ++ switch (buf[2]) { ++ case '0': ++ case '1': ++ case '2': khandy.ena = buf[2]-'0'; return count; ++ } ++ } ++ ++ if (buf[0] == 't' && count == 258) { ++ memcpy(act_scancode,buf+2,256); ++ /* rescan cursor left/right and del */ ++ clps711x_handykey_init(); ++ return count; ++ } ++ ++ return -EINVAL; ++} ++ ++ ++/* ++ * Initialize the keyboard hardware. ++ * Set all columns high ++ * Install interrupt handler ++ * ++ * Machine dependent parameters: ++ * ++ * fastscan: 0 = timer based scan for each column ++ * 1 = full scan is done in one timer event ++ * scan_delay: time between column scans ++ * setup even if you use fastscan (leeds to timer mode) ++ * scan_interval: time between full scans ++ * handy.delay: timeout before last entry get's automatically valid ++ * ++ */ ++void __init clps711x_kbd_init_hw(void) ++{ ++ ++ /* ++ * put here machine dependent init stuff ++ */ ++ if (machine_is_autcpu12()) { ++ fastscan = 0; ++ scan_interval = 50*HZ/1000; ++ scan_delay = 20*HZ/1000; ++ khandy.delay = 750*HZ/1000; ++ act_scancode = autcpu12_scancode; ++ } else { ++ printk("No initialization, keyboard killed\n"); ++ return; ++ } ++ ++ last_column = KBSC_COL0; ++ key_is_pressed = 0; ++ ++ clps711x_handykey_init(); ++ ++ /* Register the /proc entry */ ++ clps711x_keyboard_proc_entry = create_proc_entry("keyboard", 0444, ++ &proc_root); ++ if (clps711x_keyboard_proc_entry == NULL) ++ printk("Couldn't create the /proc entry for the keyboard\n"); ++ else { ++ clps711x_keyboard_proc_entry->read_proc = ++ &clps711x_kbd_proc_keyboard_read; ++ clps711x_keyboard_proc_entry->write_proc = ++ &clps711x_kbd_proc_keyboard_write; ++ } ++ ++ /* Initialize the matrix processing task. */ ++ k_translate = clps711x_translate; ++ k_unexpected_up = clps711x_unexpected_up; ++ kbd_process_task.routine = clps711x_kbd_process; ++ kbd_process_task.data = 0; ++ ++ /* Setup the timer for keyboard polling, after kbd int */ ++ init_timer(&clps711x_kbd_timer); ++ clps711x_kbd_timer.function = clps711x_kbd_timeout; ++ clps711x_kbd_timer.data = 0; ++ init_timer(&clps711x_kbdhandy_timer); ++ clps711x_kbdhandy_timer.function = clps711x_kbdhandy_timeout; ++ clps711x_kbdhandy_timer.data = 1; ++ ++ /* Initialise scan hardware, request int */ ++ clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) ++ | KBSC_HI, SYSCON1); ++ request_irq(IRQ_KBDINT, clps711x_kbd_int, 0,"keyboard", NULL); ++ ++ printk("clps711x keyboard init done\n"); ++ ++} +diff -urN linux-2.4.26/drivers/char/console.c linux-2.4.26-vrs1/drivers/char/console.c +--- linux-2.4.26/drivers/char/console.c 2003-11-28 18:26:20.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/char/console.c 2004-01-14 21:38:59.000000000 +0000 +@@ -72,8 +72,14 @@ + * + * Removed console_lock, enabled interrupts across all console operations + * 13 March 2001, Andrew Morton ++ * ++ * Split out con_write_ctrl_* functions from do_con_write & changed ++ * vc_state to function pointer ++ * by Russell King , July 1998 + */ + ++#define CONSOLE_WIP ++ + #include + #include + #include +@@ -228,7 +234,7 @@ + static inline unsigned short *screenpos(int currcons, int offset, int viewed) + { + unsigned short *p; +- ++ + if (!viewed) + p = (unsigned short *)(origin + offset); + else if (!sw->con_screen_pos) +@@ -729,7 +735,7 @@ + else { + unsigned short *p = (unsigned short *) kmalloc(ss, GFP_USER); + if (!p) { +- for (i = first; i < currcons; i++) ++ for (i = first; i< currcons; i++) + if (newscreens[i]) + kfree(newscreens[i]); + return -ENOMEM; +@@ -847,7 +853,7 @@ + /* the default colour table, for VGA+ colour systems */ + int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa, + 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff}; +-int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa, ++int default_grn[] = {0x00,0x00,0xaa,0xaa,0x00,0x00,0xaa,0xaa, + 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff}; + int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa, + 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff}; +@@ -1393,6 +1399,19 @@ + need_wrap = 0; + } + ++static int con_write_ctrl_ESnormal(int, struct tty_struct *, unsigned int); ++static int con_write_ctrl_ESesc(int, struct tty_struct *, unsigned int); ++static int con_write_ctrl_ESnonstd(int, struct tty_struct *, unsigned int); ++static int con_write_ctrl_ESpalette(int, struct tty_struct *, unsigned int); ++static int con_write_ctrl_ESsquare(int, struct tty_struct *, unsigned int); ++static int con_write_ctrl_ESgetpars(int, struct tty_struct *, unsigned int); ++static int con_write_ctrl_ESgotpars(int, struct tty_struct *, unsigned int); ++static int con_write_ctrl_ESpercent(int, struct tty_struct *, unsigned int); ++static int con_write_ctrl_ESfunckey(int, struct tty_struct *, unsigned int); ++static int con_write_ctrl_EShash(int, struct tty_struct *, unsigned int); ++static int con_write_ctrl_ESsetG0(int, struct tty_struct *, unsigned int); ++static int con_write_ctrl_ESsetG1(int, struct tty_struct *, unsigned int); ++ + enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, + EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd, + ESpalette }; +@@ -1402,7 +1421,7 @@ + { + top = 0; + bottom = video_num_lines; +- vc_state = ESnormal; ++ vc_state = con_write_ctrl_ESnormal; + ques = 0; + translate = set_translate(LAT1_MAP,currcons); + G0_charset = LAT1_MAP; +@@ -1500,328 +1519,426 @@ + disp_ctrl = 0; + return; + case 24: case 26: +- vc_state = ESnormal; ++ vc_state = con_write_ctrl_ESnormal; + return; + case 27: +- vc_state = ESesc; ++ vc_state = con_write_ctrl_ESesc; + return; + case 127: + del(currcons); + return; + case 128+27: +- vc_state = ESsquare; ++ vc_state = con_write_ctrl_ESsquare; + return; + } +- switch(vc_state) { +- case ESesc: +- vc_state = ESnormal; +- switch (c) { +- case '[': +- vc_state = ESsquare; +- return; +- case ']': +- vc_state = ESnonstd; +- return; +- case '%': +- vc_state = ESpercent; +- return; +- case 'E': +- cr(currcons); +- lf(currcons); +- return; +- case 'M': +- ri(currcons); +- return; +- case 'D': +- lf(currcons); +- return; +- case 'H': +- tab_stop[x >> 5] |= (1 << (x & 31)); +- return; +- case 'Z': +- respond_ID(tty); +- return; +- case '7': +- save_cur(currcons); +- return; +- case '8': +- restore_cur(currcons); +- return; +- case '(': +- vc_state = ESsetG0; +- return; +- case ')': +- vc_state = ESsetG1; +- return; +- case '#': +- vc_state = EShash; +- return; +- case 'c': +- reset_terminal(currcons,1); +- return; +- case '>': /* Numeric keypad */ +- clr_kbd(kbdapplic); +- return; +- case '=': /* Appl. keypad */ +- set_kbd(kbdapplic); +- return; ++ vc_state(currcons, tty, c); ++} ++ ++static int con_write_utf(int currcons, int c) ++{ ++ unsigned int chr; ++ ++ /* Combine UTF-8 into Unicode */ ++ /* Incomplete characters silently ignored */ ++ if (c < 0x80) { ++ utf_count = 0; ++ return c; ++ } ++ ++ if (utf_count > 0 && (c & 0xc0) == 0x80) { ++ chr = (utf_char << 6) | (c & 0x3f); ++ utf_count--; ++ if (utf_count == 0) ++ return chr; ++ } else { ++ unsigned int count; ++ if ((c & 0xe0) == 0xc0) { ++ count = 1; ++ chr = (c & 0x1f); ++ } else if ((c & 0xf0) == 0xe0) { ++ count = 2; ++ chr = (c & 0x0f); ++ } else if ((c & 0xf8) == 0xf0) { ++ count = 3; ++ chr = (c & 0x07); ++ } else if ((c & 0xfc) == 0xf8) { ++ count = 4; ++ chr = (c & 0x03); ++ } else if ((c & 0xfe) == 0xfc) { ++ count = 5; ++ chr = (c & 0x01); ++ } else { ++ count = 0; ++ chr = 0; + } +- return; +- case ESnonstd: +- if (c=='P') { /* palette escape sequence */ +- for (npar=0; npar='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) { +- par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ; +- if (npar==7) { +- int i = par[0]*3, j = 1; +- palette[i] = 16*par[j++]; +- palette[i++] += par[j++]; +- palette[i] = 16*par[j++]; +- palette[i++] += par[j++]; +- palette[i] = 16*par[j++]; +- palette[i] += par[j]; +- set_palette(currcons); +- vc_state = ESnormal; +- } +- } else +- vc_state = ESnormal; +- return; +- case ESsquare: +- for(npar = 0 ; npar < NPAR ; npar++) +- par[npar] = 0; +- npar = 0; +- vc_state = ESgetpars; +- if (c == '[') { /* Function key */ +- vc_state=ESfunckey; +- return; ++ utf_count = count; ++ } ++ ++ utf_char = chr; ++ return -1; ++} ++ ++static int con_write_ctrl_ESnormal(int currcons, struct tty_struct *tty, unsigned int c) ++{ ++ return 0; ++} ++ ++static int con_write_ctrl_ESesc(int currcons, struct tty_struct *tty, unsigned int c) ++{ ++ vc_state = con_write_ctrl_ESnormal; ++ switch (c) { ++ case '[': ++ vc_state = con_write_ctrl_ESsquare; ++ break; ++ case ']': ++ vc_state = con_write_ctrl_ESnonstd; ++ break; ++ case '%': ++ vc_state = con_write_ctrl_ESpercent; ++ break; ++ case 'E': ++ cr(currcons); ++ lf(currcons); ++ break; ++ case 'M': ++ ri(currcons); ++ break; ++ case 'D': ++ lf(currcons); ++ break; ++ case 'H': ++ tab_stop[x >> 5] |= (1 << (x & 31)); ++ break; ++ case 'Z': ++ respond_ID(tty); ++ break; ++ case '7': ++ save_cur(currcons); ++ break; ++ case '8': ++ restore_cur(currcons); ++ return 1; ++ case '(': ++ vc_state = con_write_ctrl_ESsetG0; ++ break; ++ case ')': ++ vc_state = con_write_ctrl_ESsetG1; ++ break; ++ case '#': ++ vc_state = con_write_ctrl_EShash; ++ break; ++ case 'c': ++ reset_terminal(currcons,1); ++ return 1; ++ case '>': /* Numeric keypad */ ++ clr_kbd(kbdapplic); ++ break; ++ case '=': /* Appl. keypad */ ++ set_kbd(kbdapplic); ++ break; ++ } ++ return 0; ++} ++ ++static int con_write_ctrl_ESnonstd(int currcons, struct tty_struct *tty, unsigned int c) ++{ ++ switch (c) { ++ case 'P': /* palette escape sequence */ ++ for (npar=0; npar='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) { ++ par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ; ++ if (npar==7) { ++ int i = par[0]*3, j = 1; ++ palette[i] = 16*par[j++]; ++ palette[i++] += par[j++]; ++ palette[i] = 16*par[j++]; ++ palette[i++] += par[j++]; ++ palette[i] = 16*par[j++]; ++ palette[i] += par[j]; ++ set_palette(currcons); ++ vc_state = con_write_ctrl_ESnormal; + } +- ques = (c=='?'); +- if (ques) +- return; +- case ESgetpars: +- if (c==';' && npar='0' && c<='9') { +- par[npar] *= 10; +- par[npar] += c-'0'; +- return; +- } else vc_state=ESgotpars; +- case ESgotpars: +- vc_state = ESnormal; +- switch(c) { +- case 'h': +- set_mode(currcons,1); +- return; +- case 'l': +- set_mode(currcons,0); +- return; +- case 'c': +- if (ques) { +- if (par[0]) +- cursor_type = par[0] | (par[1]<<8) | (par[2]<<16); +- else +- cursor_type = CUR_DEFAULT; +- return; +- } +- break; +- case 'm': +- if (ques) { +- clear_selection(); +- if (par[0]) +- complement_mask = par[0]<<8 | par[1]; +- else +- complement_mask = s_complement_mask; +- return; +- } +- break; +- case 'n': +- if (!ques) { +- if (par[0] == 5) +- status_report(tty); +- else if (par[0] == 6) +- cursor_report(currcons,tty); +- } +- return; ++ } else ++ vc_state = con_write_ctrl_ESnormal; ++ return 0; ++} ++ ++static int con_write_ctrl_ESsquare(int currcons, struct tty_struct *tty, unsigned int c) ++{ ++ for(npar = 0 ; npar < NPAR ; npar++) ++ par[npar] = 0; ++ npar = 0; ++ vc_state = con_write_ctrl_ESgetpars; ++ if (c == '[') { /* Function key */ ++ vc_state = con_write_ctrl_ESfunckey; ++ return 0; ++ } ++ ques = (c=='?'); ++ if (ques) ++ return 0; ++ return con_write_ctrl_ESgetpars(currcons, tty, c); ++} ++ ++static int con_write_ctrl_ESgetpars(int currcons, struct tty_struct *tty, unsigned int c) ++{ ++ if (c==';' && npar='0' && c<='9') { ++ par[npar] *= 10; ++ par[npar] += c-'0'; ++ return 0; ++ } else vc_state = con_write_ctrl_ESgotpars; ++ return con_write_ctrl_ESgotpars(currcons, tty, c); ++} ++ ++static int con_write_ctrl_ESgotpars(int currcons, struct tty_struct *tty, unsigned int c) ++{ ++ vc_state = con_write_ctrl_ESnormal; ++ switch(c) { ++ case 'h': ++ set_mode(currcons,1); ++ return 0; ++ case 'l': ++ set_mode(currcons,0); ++ return 0; ++ case 'c': ++ if (ques) { ++ if (par[0]) ++ cursor_type = par[0] | (par[1]<<8) | (par[2]<<16); ++ else ++ cursor_type = CUR_DEFAULT; ++ return 0; + } ++ break; ++ case 'm': + if (ques) { +- ques = 0; +- return; ++ clear_selection(); ++ if (par[0]) ++ complement_mask = par[0]<<8 | par[1]; ++ else ++ complement_mask = s_complement_mask; ++ return 0; + } +- switch(c) { +- case 'G': case '`': +- if (par[0]) par[0]--; +- gotoxy(currcons,par[0],y); +- return; +- case 'A': +- if (!par[0]) par[0]++; +- gotoxy(currcons,x,y-par[0]); +- return; +- case 'B': case 'e': +- if (!par[0]) par[0]++; +- gotoxy(currcons,x,y+par[0]); +- return; +- case 'C': case 'a': +- if (!par[0]) par[0]++; +- gotoxy(currcons,x+par[0],y); +- return; +- case 'D': +- if (!par[0]) par[0]++; +- gotoxy(currcons,x-par[0],y); +- return; +- case 'E': +- if (!par[0]) par[0]++; +- gotoxy(currcons,0,y+par[0]); +- return; +- case 'F': +- if (!par[0]) par[0]++; +- gotoxy(currcons,0,y-par[0]); +- return; +- case 'd': +- if (par[0]) par[0]--; +- gotoxay(currcons,x,par[0]); +- return; +- case 'H': case 'f': +- if (par[0]) par[0]--; +- if (par[1]) par[1]--; +- gotoxay(currcons,par[1],par[0]); +- return; +- case 'J': +- csi_J(currcons,par[0]); +- return; +- case 'K': +- csi_K(currcons,par[0]); +- return; +- case 'L': +- csi_L(currcons,par[0]); +- return; +- case 'M': +- csi_M(currcons,par[0]); +- return; +- case 'P': +- csi_P(currcons,par[0]); +- return; +- case 'c': +- if (!par[0]) +- respond_ID(tty); +- return; +- case 'g': +- if (!par[0]) +- tab_stop[x >> 5] &= ~(1 << (x & 31)); +- else if (par[0] == 3) { +- tab_stop[0] = +- tab_stop[1] = +- tab_stop[2] = +- tab_stop[3] = +- tab_stop[4] = 0; +- } +- return; +- case 'm': +- csi_m(currcons); +- return; +- case 'q': /* DECLL - but only 3 leds */ +- /* map 0,1,2,3 to 0,1,2,4 */ +- if (par[0] < 4) +- setledstate(kbd_table + currcons, +- (par[0] < 3) ? par[0] : 4); +- return; +- case 'r': +- if (!par[0]) +- par[0]++; +- if (!par[1]) +- par[1] = video_num_lines; +- /* Minimum allowed region is 2 lines */ +- if (par[0] < par[1] && +- par[1] <= video_num_lines) { +- top=par[0]-1; +- bottom=par[1]; +- gotoxay(currcons,0,0); +- } +- return; +- case 's': +- save_cur(currcons); +- return; +- case 'u': +- restore_cur(currcons); +- return; +- case 'X': +- csi_X(currcons, par[0]); +- return; +- case '@': +- csi_at(currcons,par[0]); +- return; +- case ']': /* setterm functions */ +- setterm_command(currcons); +- return; ++ break; ++ case 'n': ++ if (!ques) { ++ if (par[0] == 5) ++ status_report(tty); ++ else if (par[0] == 6) ++ cursor_report(currcons,tty); + } +- return; +- case ESpercent: +- vc_state = ESnormal; +- switch (c) { +- case '@': /* defined in ISO 2022 */ +- utf = 0; +- return; +- case 'G': /* prelim official escape code */ +- case '8': /* retained for compatibility */ +- utf = 1; +- return; ++ return 0; ++ } ++ if (ques) { ++ ques = 0; ++ return 0; ++ } ++ switch(c) { ++ case 'G': case '`': ++ if (par[0]) par[0]--; ++ gotoxy(currcons,par[0],y); ++ break; ++ case 'A': ++ if (!par[0]) par[0]++; ++ gotoxy(currcons,x,y-par[0]); ++ break; ++ case 'B': case 'e': ++ if (!par[0]) par[0]++; ++ gotoxy(currcons,x,y+par[0]); ++ break; ++ case 'C': case 'a': ++ if (!par[0]) par[0]++; ++ gotoxy(currcons,x+par[0],y); ++ break; ++ case 'D': ++ if (!par[0]) par[0]++; ++ gotoxy(currcons,x-par[0],y); ++ break; ++ case 'E': ++ if (!par[0]) par[0]++; ++ gotoxy(currcons,0,y+par[0]); ++ break; ++ case 'F': ++ if (!par[0]) par[0]++; ++ gotoxy(currcons,0,y-par[0]); ++ break; ++ case 'd': ++ if (par[0]) par[0]--; ++ gotoxay(currcons,x,par[0]); ++ break; ++ case 'H': case 'f': ++ if (par[0]) par[0]--; ++ if (par[1]) par[1]--; ++ gotoxay(currcons,par[1],par[0]); ++ break; ++ case 'J': ++ csi_J(currcons,par[0]); ++ break; ++ case 'K': ++ csi_K(currcons,par[0]); ++ break; ++ case 'L': ++ csi_L(currcons,par[0]); ++ break; ++ case 'M': ++ csi_M(currcons,par[0]); ++ break; ++ case 'P': ++ csi_P(currcons,par[0]); ++ break; ++ case 'c': ++ if (!par[0]) ++ respond_ID(tty); ++ break; ++ case 'g': ++ if (!par[0]) ++ tab_stop[x >> 5] &= ~(1 << (x & 31)); ++ else if (par[0] == 3) { ++ tab_stop[0] = ++ tab_stop[1] = ++ tab_stop[2] = ++ tab_stop[3] = ++ tab_stop[4] = 0; + } +- return; +- case ESfunckey: +- vc_state = ESnormal; +- return; +- case EShash: +- vc_state = ESnormal; +- if (c == '8') { +- /* DEC screen alignment test. kludge :-) */ +- video_erase_char = +- (video_erase_char & 0xff00) | 'E'; +- csi_J(currcons, 2); +- video_erase_char = +- (video_erase_char & 0xff00) | ' '; +- do_update_region(currcons, origin, screenbuf_size/2); ++ break; ++ case 'm': ++ csi_m(currcons); ++ return 1; ++ case 'q': /* DECLL - but only 3 leds */ ++ /* map 0,1,2,3 to 0,1,2,4 */ ++ if (par[0] < 4) ++ setledstate(kbd_table + currcons, ++ (par[0] < 3) ? par[0] : 4); ++ break; ++ case 'r': ++ if (!par[0]) ++ par[0]++; ++ if (!par[1]) ++ par[1] = video_num_lines; ++ /* Minimum allowed region is 2 lines */ ++ if (par[0] < par[1] && ++ par[1] <= video_num_lines) { ++ top=par[0]-1; ++ bottom=par[1]; ++ gotoxay(currcons,0,0); + } +- return; +- case ESsetG0: +- if (c == '0') +- G0_charset = GRAF_MAP; +- else if (c == 'B') +- G0_charset = LAT1_MAP; +- else if (c == 'U') +- G0_charset = IBMPC_MAP; +- else if (c == 'K') +- G0_charset = USER_MAP; +- if (charset == 0) +- translate = set_translate(G0_charset,currcons); +- vc_state = ESnormal; +- return; +- case ESsetG1: +- if (c == '0') +- G1_charset = GRAF_MAP; +- else if (c == 'B') +- G1_charset = LAT1_MAP; +- else if (c == 'U') +- G1_charset = IBMPC_MAP; +- else if (c == 'K') +- G1_charset = USER_MAP; +- if (charset == 1) +- translate = set_translate(G1_charset,currcons); +- vc_state = ESnormal; +- return; +- default: +- vc_state = ESnormal; ++ break; ++ case 's': ++ save_cur(currcons); ++ break; ++ case 'u': ++ restore_cur(currcons); ++ return 1; ++ case 'X': ++ csi_X(currcons, par[0]); ++ break; ++ case '@': ++ csi_at(currcons,par[0]); ++ break; ++ case ']': /* setterm functions */ ++ setterm_command(currcons); ++ break; ++ } ++ return 0; ++} ++ ++static int con_write_ctrl_ESpercent(int currcons, struct tty_struct *tty, unsigned int c) ++{ ++ vc_state = con_write_ctrl_ESnormal; ++ switch (c) { ++ case '@': /* defined in ISO 2022 */ ++ utf = 0; ++ break; ++ case 'G': /* prelim official escape code */ ++ case '8': /* retained for compatibility */ ++ utf = 1; ++ break; ++ } ++ return 0; ++} ++ ++static int con_write_ctrl_ESfunckey(int currcons, struct tty_struct *tty, unsigned int c) ++{ ++ vc_state = con_write_ctrl_ESnormal; ++ return 0; ++} ++ ++static int con_write_ctrl_EShash(int currcons, struct tty_struct *tty, unsigned int c) ++{ ++ vc_state = con_write_ctrl_ESnormal; ++ if (c == '8') { ++ /* DEC screen alignment test. kludge :-) */ ++ video_erase_char = ++ (video_erase_char & 0xff00) | 'E'; ++ csi_J(currcons, 2); ++ video_erase_char = ++ (video_erase_char & 0xff00) | ' '; ++ do_update_region(currcons, origin, screenbuf_size/2); ++ } ++ return 0; ++} ++ ++static int con_write_ctrl_ESsetG0(int currcons, struct tty_struct *tty, unsigned int c) ++{ ++ switch (c) { ++ case '0': ++ G0_charset = GRAF_MAP; ++ break; ++ case 'B': ++ G0_charset = LAT1_MAP; ++ break; ++ case 'U': ++ G0_charset = IBMPC_MAP; ++ break; ++ case 'K': ++ G0_charset = USER_MAP; ++ break; ++ } ++ if (charset == 0) { ++ translate = set_translate(G0_charset,currcons); ++ return 1; ++ } ++ vc_state = con_write_ctrl_ESnormal; ++ return 0; ++} ++ ++static int con_write_ctrl_ESsetG1(int currcons, struct tty_struct *tty, unsigned int c) ++{ ++ switch (c) { ++ case '0': ++ G1_charset = GRAF_MAP; ++ break; ++ case 'B': ++ G1_charset = LAT1_MAP; ++ break; ++ case 'U': ++ G1_charset = IBMPC_MAP; ++ break; ++ case 'K': ++ G1_charset = USER_MAP; ++ break; ++ } ++ if (charset == 1) { ++ translate = set_translate(G1_charset,currcons); ++ return 1; + } ++ vc_state = con_write_ctrl_ESnormal; ++ return 0; + } + + /* This is a temporary buffer used to prepare a tty console write +@@ -1855,7 +1972,7 @@ + unsigned long draw_from = 0, draw_to = 0; + struct vt_struct *vt = (struct vt_struct *)tty->driver_data; + u16 himask, charmask; +- const unsigned char *orig_buf = NULL; ++ const unsigned char *orig_buf; + int orig_count; + + if (in_interrupt()) +@@ -1913,42 +2030,12 @@ + count--; + + if (utf) { +- /* Combine UTF-8 into Unicode */ +- /* Incomplete characters silently ignored */ +- if(c > 0x7f) { +- if (utf_count > 0 && (c & 0xc0) == 0x80) { +- utf_char = (utf_char << 6) | (c & 0x3f); +- utf_count--; +- if (utf_count == 0) +- tc = c = utf_char; +- else continue; +- } else { +- if ((c & 0xe0) == 0xc0) { +- utf_count = 1; +- utf_char = (c & 0x1f); +- } else if ((c & 0xf0) == 0xe0) { +- utf_count = 2; +- utf_char = (c & 0x0f); +- } else if ((c & 0xf8) == 0xf0) { +- utf_count = 3; +- utf_char = (c & 0x07); +- } else if ((c & 0xfc) == 0xf8) { +- utf_count = 4; +- utf_char = (c & 0x03); +- } else if ((c & 0xfe) == 0xfc) { +- utf_count = 5; +- utf_char = (c & 0x01); +- } else +- utf_count = 0; ++ tc = con_write_utf(currcons, c); ++ if (tc < 0) + continue; +- } +- } else { +- tc = c; +- utf_count = 0; +- } +- } else { /* no utf */ +- tc = translate[toggle_meta ? (c|0x80) : c]; +- } ++ c = tc; ++ } else /* no utf */ ++ tc = translate[toggle_meta ? (c|0x80) : c]; + + /* If the original code was a control character we + * only allow a glyph to be displayed if the code is +@@ -1966,7 +2053,7 @@ + && (c != 127 || disp_ctrl) + && (c != 128+27); + +- if (vc_state == ESnormal && ok) { ++ if (vc_state == con_write_ctrl_ESnormal && ok) { + /* Now try to find out how to display it */ + tc = conv_uni_to_pc(vc_cons[currcons].d, tc); + if ( tc == -4 ) { +@@ -2112,7 +2199,7 @@ + if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1)) + currcons = kmsg_redirect - 1; + +- /* read `x' only after setting currecons properly (otherwise ++ /* read `x' only after setting currcons properly (otherwise + the `x' macro will read the x of the foreground console). */ + myx = x; + +@@ -2475,8 +2562,8 @@ + console_driver.init_termios = tty_std_termios; + console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; + /* Tell tty_register_driver() to skip consoles because they are +- * registered before kmalloc() is ready. We'll patch them in later. +- * See comments at console_init(); see also con_init_devfs(). ++ * registered before kmalloc() is ready. We'll patch them in later. ++ * See comments at console_init(); see also con_init_devfs(). + */ + console_driver.flags |= TTY_DRIVER_NO_DEVFS; + console_driver.refcount = &console_refcount; +@@ -2719,7 +2806,7 @@ + + if (console_blank_hook && console_blank_hook(1)) + return; +- if (vesa_blank_mode) ++ if (vesa_blank_mode) + sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1); + } + +@@ -2893,7 +2980,7 @@ + if (!op->height) { /* Need to guess font height [compat] */ + int h, i; + u8 *charmap = op->data, tmp; +- ++ + /* If from KDFONTOP ioctl, don't allow things which can be done in userland, + so that we can get rid of this soon */ + if (!(op->flags & KD_FONT_FLAG_OLD)) +@@ -2940,18 +3027,18 @@ + op->data = old_op.data; + if (!rc && !set) { + int c = (op->width+7)/8 * 32 * op->charcount; +- ++ + if (op->data && op->charcount > old_op.charcount) + rc = -ENOSPC; + if (!(op->flags & KD_FONT_FLAG_OLD)) { +- if (op->width > old_op.width || ++ if (op->width > old_op.width || + op->height > old_op.height) + rc = -ENOSPC; + } else { + if (op->width != 8) + rc = -EIO; + else if ((old_op.height && op->height > old_op.height) || +- op->height > 32) ++ op->height > 32) + rc = -ENOSPC; + } + if (!rc && op->data && copy_to_user(op->data, temp, c)) +diff -urN linux-2.4.26/drivers/char/ds1307.c linux-2.4.26-vrs1/drivers/char/ds1307.c +--- linux-2.4.26/drivers/char/ds1307.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/ds1307.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,604 @@ ++/* ++ * ds1307.c ++ * ++ * Device driver for Dallas Semiconductor's Real Time Controller DS1307. ++ * ++ * Copyright (C) 2002 Intrinsyc Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ds1307.h" ++ ++#define DEBUG 0 ++ ++#if DEBUG ++static unsigned int rtc_debug = DEBUG; ++#else ++#define rtc_debug 0 /* gcc will remove all the debug code for us */ ++#endif ++ ++static unsigned short slave_address = DS1307_I2C_SLAVE_ADDR; ++ ++struct i2c_driver ds1307_driver; ++struct i2c_client *ds1307_i2c_client = 0; ++ ++static unsigned short ignore[] = { I2C_CLIENT_END }; ++static unsigned short normal_addr[] = { DS1307_I2C_SLAVE_ADDR, I2C_CLIENT_END }; ++ ++static struct i2c_client_address_data addr_data = { ++ normal_i2c: normal_addr, ++ normal_i2c_range: ignore, ++ probe: ignore, ++ probe_range: ignore, ++ ignore: ignore, ++ ignore_range: ignore, ++ force: ignore, ++}; ++ ++static int ds1307_rtc_ioctl( struct inode *, struct file *, unsigned int, unsigned long); ++static int ds1307_rtc_open(struct inode *inode, struct file *file); ++static int ds1307_rtc_release(struct inode *inode, struct file *file); ++ ++static struct file_operations rtc_fops = { ++ owner: THIS_MODULE, ++ ioctl: ds1307_rtc_ioctl, ++ open: ds1307_rtc_open, ++ release: ds1307_rtc_release, ++}; ++ ++static struct miscdevice ds1307_rtc_miscdev = { ++ RTC_MINOR, ++ "rtc", ++ &rtc_fops ++}; ++ ++static int ds1307_probe(struct i2c_adapter *adap); ++static int ds1307_detach(struct i2c_client *client); ++static int ds1307_command(struct i2c_client *client, unsigned int cmd, void *arg); ++ ++struct i2c_driver ds1307_driver = { ++ name: "DS1307", ++ id: I2C_DRIVERID_DS1307, ++ flags: I2C_DF_NOTIFY, ++ attach_adapter: ds1307_probe, ++ detach_client: ds1307_detach, ++ command: ds1307_command ++}; ++ ++static spinlock_t ds1307_rtc_lock = SPIN_LOCK_UNLOCKED; ++ ++#define DAT(x) ((unsigned int)((x)->data)) /* keep the control register info */ ++ ++static int ++ds1307_readram( char *buf, int len) ++{ ++ unsigned long flags; ++ unsigned char ad[1] = { 0 }; ++ int ret; ++ struct i2c_msg msgs[2] = { ++ { ds1307_i2c_client->addr , 0, 1, ad }, ++ { ds1307_i2c_client->addr , I2C_M_RD, len, buf } }; ++ ++ spin_lock_irqsave(&ds1307_rtc_lock, flags); ++ ret = i2c_transfer(ds1307_i2c_client->adapter, msgs, 2); ++ spin_unlock_irqrestore(&ds1307_rtc_lock,flags); ++ ++ return ret; ++} ++ ++static void ++ds1307_dumpram( void) ++{ ++ unsigned char buf[DS1307_RAM_SIZE]; ++ int ret; ++ ++ ret = ds1307_readram( buf, DS1307_RAM_SIZE); ++ ++ if( ret > 0) ++ { ++ int i; ++ for( i=0; iaddr , 0, 1, ad }, ++ { ds1307_i2c_client->addr , I2C_M_RD, 1, buf } ++ }; ++ unsigned char ctrl_info; ++ int ret; ++ ++ if( enable) ++ ctrl_info = SQW_ENABLE | RATE_32768HZ; ++ else ++ ctrl_info = SQW_DISABLE; ++ ds1307_command(ds1307_i2c_client, DS1307_SETCTRL, &ctrl_info); ++ ++ /* read addr 0 (Clock-Halt bit and second counter */ ++ ret = i2c_transfer(ds1307_i2c_client->adapter, msgs, 2); ++ ++ if( enable) ++ buf[1] = buf[0] & ~CLOCK_HALT; /* clear Clock-Halt bit */ ++ else ++ buf[1] = buf[0] | CLOCK_HALT; /* set Clock-Halt bit */ ++ buf[0] = 0; /* control register address on DS1307 */ ++ ++ ret = i2c_master_send(ds1307_i2c_client, (char *)buf, 2); ++} ++ ++static int ++ds1307_attach(struct i2c_adapter *adap, int addr, unsigned short flags,int kind) ++{ ++ struct i2c_client *c; ++ unsigned char buf[1], ad[1] = { 7 }; ++ struct i2c_msg msgs[2] = { ++ { addr , 0, 1, ad }, ++ { addr , I2C_M_RD, 1, buf } ++ }; ++ int ret; ++ ++ c = (struct i2c_client *)kmalloc(sizeof(*c), GFP_KERNEL); ++ if (!c) ++ return -ENOMEM; ++ ++ strcpy(c->name, "DS1307"); ++ c->id = ds1307_driver.id; ++ c->flags = 0; ++ c->addr = addr; ++ c->adapter = adap; ++ c->driver = &ds1307_driver; ++ c->data = NULL; ++ ++ ret = i2c_transfer(c->adapter, msgs, 2); ++ ++ if ( ret == 2 ) ++ { ++ DAT(c) = buf[0]; ++ } ++ else ++ printk ("ds1307_attach(): i2c_transfer() returned %d.\n",ret); ++ ++ ds1307_i2c_client = c; ++ ds1307_enable_clock( 1); ++ ++ return i2c_attach_client(c); ++} ++ ++static int ++ds1307_probe(struct i2c_adapter *adap) ++{ ++ return i2c_probe(adap, &addr_data, ds1307_attach); ++} ++ ++static int ++ds1307_detach(struct i2c_client *client) ++{ ++ i2c_detach_client(client); ++ ds1307_enable_clock( 0); ++ ++ return 0; ++} ++ ++static void ++ds1307_convert_to_time( struct rtc_time *dt, char *buf) ++{ ++ dt->tm_sec = BCD_TO_BIN(buf[0]); ++ dt->tm_min = BCD_TO_BIN(buf[1]); ++ ++ if ( TWELVE_HOUR_MODE(buf[2]) ) ++ { ++ dt->tm_hour = HOURS_12(buf[2]); ++ if (HOURS_AP(buf[2])) /* PM */ ++ { ++ dt->tm_hour += 12; ++ } ++ } ++ else /* 24-hour-mode */ ++ { ++ dt->tm_hour = HOURS_24(buf[2]); ++ } ++ ++ dt->tm_mday = BCD_TO_BIN(buf[4]); ++ /* dt->tm_mon is zero-based */ ++ dt->tm_mon = BCD_TO_BIN(buf[5]) - 1; ++ /* year is 1900 + dt->tm_year */ ++ dt->tm_year = BCD_TO_BIN(buf[6]) + 100; ++ ++ if( rtc_debug > 2) ++ { ++ printk("ds1307_get_datetime: year = %d\n", dt->tm_year); ++ printk("ds1307_get_datetime: mon = %d\n", dt->tm_mon); ++ printk("ds1307_get_datetime: mday = %d\n", dt->tm_mday); ++ printk("ds1307_get_datetime: hour = %d\n", dt->tm_hour); ++ printk("ds1307_get_datetime: min = %d\n", dt->tm_min); ++ printk("ds1307_get_datetime: sec = %d\n", dt->tm_sec); ++ } ++} ++ ++static int ++ds1307_get_datetime(struct i2c_client *client, struct rtc_time *dt) ++{ ++ unsigned char buf[7], addr[1] = { 0 }; ++ struct i2c_msg msgs[2] = { ++ { client->addr, 0, 1, addr }, ++ { client->addr, I2C_M_RD, 7, buf } ++ }; ++ int ret = -EIO; ++ ++ memset(buf, 0, sizeof(buf)); ++ ++ ret = i2c_transfer(client->adapter, msgs, 2); ++ ++ if (ret == 2) { ++ ds1307_convert_to_time( dt, buf); ++ ret = 0; ++ } ++ else ++ printk("ds1307_get_datetime(), i2c_transfer() returned %d\n",ret); ++ ++ return ret; ++} ++ ++static int ++ds1307_set_datetime(struct i2c_client *client, struct rtc_time *dt, int datetoo) ++{ ++ unsigned char buf[8]; ++ int ret, len = 4; ++ ++ if( rtc_debug > 2) ++ { ++ printk("ds1307_set_datetime: tm_year = %d\n", dt->tm_year); ++ printk("ds1307_set_datetime: tm_mon = %d\n", dt->tm_mon); ++ printk("ds1307_set_datetime: tm_mday = %d\n", dt->tm_mday); ++ printk("ds1307_set_datetime: tm_hour = %d\n", dt->tm_hour); ++ printk("ds1307_set_datetime: tm_min = %d\n", dt->tm_min); ++ printk("ds1307_set_datetime: tm_sec = %d\n", dt->tm_sec); ++ } ++ ++ buf[0] = 0; /* register address on DS1307 */ ++ buf[1] = (BIN_TO_BCD(dt->tm_sec)); ++ buf[2] = (BIN_TO_BCD(dt->tm_min)); ++ buf[3] = (BIN_TO_BCD(dt->tm_hour)); ++ ++ if (datetoo) { ++ len = 8; ++ /* we skip buf[4] as we don't use day-of-week. */ ++ buf[5] = (BIN_TO_BCD(dt->tm_mday)); ++ buf[6] = (BIN_TO_BCD(dt->tm_mon + 1)); ++ /* The year only ranges from 0-99, we are being passed an offset from 1900, ++ * and the chip calulates leap years based on 2000, thus we adjust by 100. ++ */ ++ buf[7] = (BIN_TO_BCD(dt->tm_year - 100)); ++ } ++ ret = i2c_master_send(client, (char *)buf, len); ++ if (ret == len) ++ ret = 0; ++ else ++ printk("ds1307_set_datetime(), i2c_master_send() returned %d\n",ret); ++ ++ ++ return ret; ++} ++ ++static int ++ds1307_get_ctrl(struct i2c_client *client, unsigned char *ctrl) ++{ ++ *ctrl = DAT(client); ++ ++ return 0; ++} ++ ++static int ++ds1307_set_ctrl(struct i2c_client *client, unsigned char *cinfo) ++{ ++ unsigned char buf[2]; ++ int ret; ++ ++ ++ buf[0] = 7; /* control register address on DS1307 */ ++ buf[1] = *cinfo; ++ /* save the control reg info in the client data field so that get_ctrl ++ * function doesn't have to do an I2C transfer to get it. ++ */ ++ DAT(client) = buf[1]; ++ ++ ret = i2c_master_send(client, (char *)buf, 2); ++ ++ return ret; ++} ++ ++static int ++ds1307_read_mem(struct i2c_client *client, struct rtc_mem *mem) ++{ ++ unsigned char addr[1]; ++ struct i2c_msg msgs[2] = { ++ { client->addr, 0, 1, addr }, ++ { client->addr, I2C_M_RD, mem->nr, mem->data } ++ }; ++ ++ if ( (mem->loc < DS1307_RAM_ADDR_START) || ++ ((mem->loc + mem->nr -1) > DS1307_RAM_ADDR_END) ) ++ return -EINVAL; ++ ++ addr[0] = mem->loc; ++ ++ return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO; ++} ++ ++static int ++ds1307_write_mem(struct i2c_client *client, struct rtc_mem *mem) ++{ ++ unsigned char addr[1]; ++ struct i2c_msg msgs[2] = { ++ { client->addr, 0, 1, addr }, ++ { client->addr, 0, mem->nr, mem->data } ++ }; ++ ++ if ( (mem->loc < DS1307_RAM_ADDR_START) || ++ ((mem->loc + mem->nr -1) > DS1307_RAM_ADDR_END) ) ++ return -EINVAL; ++ ++ addr[0] = mem->loc; ++ ++ return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO; ++} ++ ++static int ++ds1307_command(struct i2c_client *client, unsigned int cmd, void *arg) ++{ ++ switch (cmd) { ++ case DS1307_GETDATETIME: ++ return ds1307_get_datetime(client, arg); ++ ++ case DS1307_SETTIME: ++ return ds1307_set_datetime(client, arg, 0); ++ ++ case DS1307_SETDATETIME: ++ return ds1307_set_datetime(client, arg, 1); ++ ++ case DS1307_GETCTRL: ++ return ds1307_get_ctrl(client, arg); ++ ++ case DS1307_SETCTRL: ++ return ds1307_set_ctrl(client, arg); ++ ++ case DS1307_MEM_READ: ++ return ds1307_read_mem(client, arg); ++ ++ case DS1307_MEM_WRITE: ++ return ds1307_write_mem(client, arg); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int ++ds1307_rtc_open(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static int ++ds1307_rtc_release(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static int ++ds1307_rtc_ioctl( struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ unsigned long flags; ++ struct rtc_time wtime; ++ int status = 0; ++ ++ switch (cmd) { ++ default: ++ case RTC_UIE_ON: ++ case RTC_UIE_OFF: ++ case RTC_PIE_ON: ++ case RTC_PIE_OFF: ++ case RTC_AIE_ON: ++ case RTC_AIE_OFF: ++ case RTC_ALM_SET: ++ case RTC_ALM_READ: ++ case RTC_IRQP_READ: ++ case RTC_IRQP_SET: ++ case RTC_EPOCH_READ: ++ case RTC_EPOCH_SET: ++ case RTC_WKALM_SET: ++ case RTC_WKALM_RD: ++ status = -EINVAL; ++ break; ++ ++ case RTC_RD_TIME: ++ spin_lock_irqsave(&ds1307_rtc_lock, flags); ++ ds1307_command( ds1307_i2c_client, DS1307_GETDATETIME, &wtime); ++ spin_unlock_irqrestore(&ds1307_rtc_lock,flags); ++ ++ if( copy_to_user((void *)arg, &wtime, sizeof (struct rtc_time))) ++ status = -EFAULT; ++ break; ++ ++ case RTC_SET_TIME: ++ if (!capable(CAP_SYS_TIME)) ++ { ++ status = -EACCES; ++ break; ++ } ++ ++ if (copy_from_user(&wtime, (struct rtc_time *)arg, sizeof(struct rtc_time)) ) ++ { ++ status = -EFAULT; ++ break; ++ } ++ ++ spin_lock_irqsave(&ds1307_rtc_lock, flags); ++ ds1307_command( ds1307_i2c_client, DS1307_SETDATETIME, &wtime); ++ spin_unlock_irqrestore(&ds1307_rtc_lock,flags); ++ break; ++ } ++ ++ return status; ++} ++ ++static char * ++ds1307_mon2str( unsigned int mon) ++{ ++ char *mon2str[12] = { ++ "Jan", "Feb", "Mar", "Apr", "May", "Jun", ++ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ++ }; ++ if( mon > 11) return "error"; ++ else return mon2str[ mon]; ++} ++ ++static int ds1307_rtc_proc_output( char *buf) ++{ ++#define CHECK(ctrl,bit) ((ctrl & bit) ? "yes" : "no") ++ unsigned char ram[DS1307_RAM_SIZE]; ++ int ret; ++ ++ char *p = buf; ++ ++ ret = ds1307_readram( ram, DS1307_RAM_SIZE); ++ if( ret > 0) ++ { ++ int i; ++ struct rtc_time dt; ++ char text[9]; ++ ++ p += sprintf(p, "DS1307 (64x8 Serial Real Time Clock)\n"); ++ ++ ds1307_convert_to_time( &dt, ram); ++ p += sprintf(p, "Date/Time : %02d-%s-%04d %02d:%02d:%02d\n", ++ dt.tm_mday, ds1307_mon2str(dt.tm_mon), dt.tm_year + 1900, ++ dt.tm_hour, dt.tm_min, dt.tm_sec); ++ ++ p += sprintf(p, "Clock halted : %s\n", CHECK(ram[0],0x80)); ++ p += sprintf(p, "24h mode : %s\n", CHECK(ram[2],0x40)); ++ p += sprintf(p, "Square wave enabled : %s\n", CHECK(ram[7],0x10)); ++ p += sprintf(p, "Freq : "); ++ ++ switch( ram[7] & 0x03) ++ { ++ case RATE_1HZ: ++ p += sprintf(p, "1Hz\n"); ++ break; ++ case RATE_4096HZ: ++ p += sprintf(p, "4.096kHz\n"); ++ break; ++ case RATE_8192HZ: ++ p += sprintf(p, "8.192kHz\n"); ++ break; ++ case RATE_32768HZ: ++ default: ++ p += sprintf(p, "32.768kHz\n"); ++ break; ++ ++ } ++ ++ p += sprintf(p, "RAM dump:\n"); ++ text[8]='\0'; ++ for( i=0; i126)) ram[i]='.'; ++ text[i%8] = ram[i]; ++ if( (i%8) == 7) p += sprintf(p, "%s\n",text); ++ } ++ p += sprintf(p, "\n"); ++ } ++ else ++ { ++ p += sprintf(p, "Failed to read RTC memory!\n"); ++ } ++ ++ return p - buf; ++} ++ ++static int ds1307_rtc_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int len = ds1307_rtc_proc_output (page); ++ if (len <= off+count) *eof = 1; ++ *start = page + off; ++ len -= off; ++ if (len>count) len = count; ++ if (len<0) len = 0; ++ return len; ++} ++ ++static __init int ds1307_init(void) ++{ ++ int retval=0; ++ ++ if( slave_address != 0xffff) ++ { ++ normal_addr[0] = slave_address; ++ } ++ ++ if( normal_addr[0] == 0xffff) ++ { ++ printk(KERN_ERR"I2C: Invalid slave address for DS1307 RTC (%#x)\n", ++ normal_addr[0]); ++ return -EINVAL; ++ } ++ ++ retval = i2c_add_driver(&ds1307_driver); ++ ++ if (retval==0) ++ { ++ misc_register (&ds1307_rtc_miscdev); ++ create_proc_read_entry (PROC_DS1307_NAME, 0, 0, ds1307_rtc_read_proc, NULL); ++ printk("I2C: DS1307 RTC driver successfully loaded\n"); ++ ++ if( rtc_debug) ds1307_dumpram(); ++ } ++ return retval; ++} ++ ++static __exit void ds1307_exit(void) ++{ ++ remove_proc_entry (PROC_DS1307_NAME, NULL); ++ misc_deregister(&ds1307_rtc_miscdev); ++ i2c_del_driver(&ds1307_driver); ++} ++ ++module_init(ds1307_init); ++module_exit(ds1307_exit); ++ ++MODULE_PARM (slave_address, "i"); ++MODULE_PARM_DESC (slave_address, "I2C slave address for DS1307 RTC."); ++ ++MODULE_AUTHOR ("Intrinsyc Software Inc."); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/char/ds1307.h linux-2.4.26-vrs1/drivers/char/ds1307.h +--- linux-2.4.26/drivers/char/ds1307.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/ds1307.h 2004-03-04 22:44:33.000000000 +0000 +@@ -0,0 +1,58 @@ ++/* ++ * ds1307.h ++ * ++ * Copyright (C) 2002 Intrinsyc Software Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++#ifndef DS1307_H ++#define DS1307_H ++ ++#if defined(CONFIG_PXA_EMERSON_SBC) || defined(CONFIG_PXA_CERF_BOARD) || defined(CONFIG_MACH_CSB337) ++ #define DS1307_I2C_SLAVE_ADDR 0x68 ++#else ++ #define DS1307_I2C_SLAVE_ADDR 0xffff ++#endif ++ ++#define DS1307_RAM_ADDR_START 0x08 ++#define DS1307_RAM_ADDR_END 0x3F ++#define DS1307_RAM_SIZE 0x40 ++ ++#define PROC_DS1307_NAME "driver/ds1307" ++ ++struct rtc_mem { ++ unsigned int loc; ++ unsigned int nr; ++ unsigned char *data; ++}; ++ ++#define DS1307_GETDATETIME 0 ++#define DS1307_SETTIME 1 ++#define DS1307_SETDATETIME 2 ++#define DS1307_GETCTRL 3 ++#define DS1307_SETCTRL 4 ++#define DS1307_MEM_READ 5 ++#define DS1307_MEM_WRITE 6 ++ ++#define SQW_ENABLE 0x10 /* Square Wave Enable */ ++#define SQW_DISABLE 0x00 /* Square Wave disable */ ++ ++#define RATE_32768HZ 0x03 /* Rate Select 32.768KHz */ ++#define RATE_8192HZ 0x02 /* Rate Select 8.192KHz */ ++#define RATE_4096HZ 0x01 /* Rate Select 4.096KHz */ ++#define RATE_1HZ 0x00 /* Rate Select 1Hz */ ++ ++#define CLOCK_HALT 0x80 /* Clock Halt */ ++ ++#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10) ++#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10) ++ ++#define TWELVE_HOUR_MODE(n) (((n)>>6)&1) ++#define HOURS_AP(n) (((n)>>5)&1) ++#define HOURS_12(n) BCD_TO_BIN((n)&0x1F) ++#define HOURS_24(n) BCD_TO_BIN((n)&0x3F) ++ ++#endif +diff -urN linux-2.4.26/drivers/char/edb7211_keyb.c linux-2.4.26-vrs1/drivers/char/edb7211_keyb.c +--- linux-2.4.26/drivers/char/edb7211_keyb.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/edb7211_keyb.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,335 @@ ++/* ++ * drivers/char/edb7211_keyb.c ++ * ++ * Copyright (C) 2000 Blue Mug, Inc. All Rights Reserved. ++ * ++ * EDB7211 Keyboard driver for ARM Linux. ++ * ++ * The EP7211 keyboard hardware only supports generating interrupts for 64 keys. ++ * The EBD7211's keyboard has 84 keys. Therefore we need to poll for keys, ++ * instead of waiting for interrupts. ++ * ++ * In a real-world hardware situation, this would be a bad thing. It would ++ * kill power management. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++ ++/* ++ * The number of jiffies between keyboard scans. ++ */ ++#define KEYBOARD_SCAN_INTERVAL 5 ++ ++/* ++ * Values for the keyboard column scan control register. ++ */ ++#define KBSC_HI 0x0 /* All driven high */ ++#define KBSC_LO 0x1 /* All driven low */ ++#define KBSC_X 0x2 /* All high impedance */ ++#define KBSC_COL0 0x8 /* Column 0 high, others high impedance */ ++#define KBSC_COL1 0x9 /* Column 1 high, others high impedance */ ++#define KBSC_COL2 0xa /* Column 2 high, others high impedance */ ++#define KBSC_COL3 0xb /* Column 3 high, others high impedance */ ++#define KBSC_COL4 0xc /* Column 4 high, others high impedance */ ++#define KBSC_COL5 0xd /* Column 5 high, others high impedance */ ++#define KBSC_COL6 0xe /* Column 6 high, others high impedance */ ++#define KBSC_COL7 0xf /* Column 7 high, others high impedance */ ++ ++ ++/* XXX: Figure out what these values should be... */ ++/* Simple translation table for the SysRq keys */ ++#ifdef CONFIG_MAGIC_SYSRQ ++unsigned char edb7211_kbd_sysrq_xlate[128] = ++ "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ ++ "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ ++ "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ ++ "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ ++ "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ ++ "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ ++ "\r\000/"; /* 0x60 - 0x6f */ ++#endif ++ ++/* ++ * Row/column to scancode mappings. ++ * ++ * This table maps row/column keyboard matrix positions to XT scancodes. ++ * ++ * The port A rows come first, followed by the extended rows. ++ */ ++static unsigned char colrow_2_scancode[128] = ++{ ++/* Column: ++ Row 0 1 2 3 4 5 6 7 */ ++/* A0 */ 0x01, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x40, 0x41, ++/* A1 */ 0x02, 0x07, 0x06, 0x05, 0x04, 0x03, 0x08, 0x09, ++/* A2 */ 0x0f, 0x14, 0x13, 0x12, 0x11, 0x10, 0x15, 0x16, ++/* A3 */ 0x3a, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x23, 0x24, ++/* A4 */ 0x29, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x31, 0x32, ++/* A5 */ 0x39, 0x35, 0x6F, 0x52, 0x00, 0x6B, 0x34, 0x33, ++/* A6 */ 0x6A, 0x27, 0x28, 0x00, 0x1c, 0x6D, 0x26, 0x25, ++/* A7 */ 0x67, 0x19, 0x1a, 0x1b, 0x2b, 0x68, 0x18, 0x17, ++/* E0 */ 0x6C, 0x0c, 0x0d, 0x0e, 0x00, 0x66, 0x0b, 0x0a, ++/* E1 */ 0x69, 0x44, 0x45, 0x37, 0x46, 0x77, 0x43, 0x42, ++/* E2 */ 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* E3 */ 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* E4 */ 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* E5 */ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* E6 */ 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++/* E7 */ 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++}; ++ ++/* ++ * A bitfield array which contains the state of the keyboard after the last ++ * scan. A bit set in this array corresponds to a key down. Only the lower ++ * 16 bits of each array element are used. ++ */ ++static unsigned long previous_keys[8]; ++static unsigned long keys[8]; ++ ++ ++/* This will be set to a non-zero value if a key was found to be pressed ++ * in the last scan. */ ++static int key_is_pressed; ++ ++static struct tq_struct kbd_process_task; ++static struct timer_list edb7211_kbd_timer; ++ ++/* ++ * External methods. ++ */ ++void edb7211_kbd_init_hw(void); ++ ++/* ++ * Internal methods. ++ */ ++static int edb7211_kbd_scan_matrix(u_long* keys); ++static void edb7211_kbd_timeout(unsigned long data); ++static void edb7211_kbd_process(void* data); ++ ++/* ++ * Translate a raw keycode to an XT keyboard scancode. ++ */ ++static int ++edb7211_translate(unsigned char scancode, unsigned char *keycode, ++ char raw_mode) ++{ ++ *keycode = colrow_2_scancode[scancode & 0x7f]; ++ return 1; ++} ++ ++/* ++ * Scan the keyboard matrix; for each key that is pressed, set the ++ * corresponding bit in the bitfield array. ++ * ++ * The parameter is expected to be an array of 8 32-bit values. Only the lower ++ * 16 bits of each value is used. Each value contains the row bits for the ++ * corresponding column. ++ */ ++static int ++edb7211_kbd_scan_matrix(u_long* keys) ++{ ++ int column, row, key_pressed; ++ unsigned char port_a_data, ext_port_data; ++ ++ key_pressed = 0; ++ ++ /* Drive all the columns low. */ ++ clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | KBSC_LO, ++ SYSCON1); ++ ++ for (column = 0; column < 8; column++) { ++ ++ /* Drive the column high. */ ++ clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | ++ (KBSC_COL0 + column), SYSCON1); ++ ++ /* Read port A and the extended port. */ ++ port_a_data = clps_readb(PADR) & 0xff; ++ ext_port_data = __raw_readb(EP7211_VIRT_EXTKBD) & 0xff; ++ ++ /* Drive all columns tri-state. */ ++ clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | KBSC_X, ++ SYSCON1); ++ ++ /* Look at each column in port A. */ ++ for (row=0; row < 8; row++) { ++ /* If the row's bit is set, set the bit in the bitfield. ++ * Otherwise, clear it. ++ */ ++ if (port_a_data & (1 << row)) { ++ keys[column] |= (1 << row); ++ key_pressed = 1; ++ } else { ++ keys[column] &= ~(1 << row); ++ } ++ } ++ ++ /* Look at each column in the extended port. */ ++ for (row=0; row < 8; row++) { ++ /* If the row's bit is set, set the bit in the bitfield. ++ * Otherwise, clear it. ++ */ ++ if (ext_port_data & (1 << row)) { ++ keys[column] |= (1 << (row + 8)); ++ key_pressed = 1; ++ } else { ++ keys[column] &= ~(1 << (row + 8)); ++ } ++ } ++ ++ /* ++ * Short delay: The example code for the EDB7211 runs an empty ++ * loop 256 times. At this rate, there were some spurious keys ++ * generated. I doubled the delay to let the column drives ++ * settle some. ++ */ ++ for (row=0; row < 512; row++) { } ++ } ++ ++ /* If we could use interrupts, we would drive all columns high so ++ * that interrupts will be generated on key presses. But we can't, ++ * so we leave all columns floating. ++ */ ++ clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | KBSC_X, ++ SYSCON1); ++ ++ return key_pressed; ++} ++ ++/* ++ * XXX: This is really ugly; this needs to be reworked to have less levels of ++ * indentation. ++ */ ++static void ++edb7211_kbd_timeout(unsigned long data) ++{ ++ /* Schedule the next timer event. */ ++ edb7211_kbd_timer.expires = jiffies + KEYBOARD_SCAN_INTERVAL; ++ add_timer(&edb7211_kbd_timer); ++ ++ if (edb7211_kbd_scan_matrix(keys) || key_is_pressed) { ++ queue_task(&kbd_process_task, &tq_timer); ++ } else { ++ key_is_pressed = 0; ++ } ++} ++ ++/* ++ * Process the keys that have been pressed. ++ */ ++static void ++edb7211_kbd_process(void* data) ++{ ++ int i; ++ ++ /* First check if any keys have been released. */ ++ if (key_is_pressed) { ++ for (i=0; i < 8; i++) { ++ if (previous_keys[i]) { ++ int row; ++ ++ for (row=0; row < 16; row++) { ++ if ((previous_keys[i] & (1 << row)) && ++ !(keys[i] & (1 << row))) { ++ /* Generate the up event. */ ++ handle_scancode( ++ (row<<3)+i, 0); ++ } ++ } ++ } ++ } ++ } ++ ++ key_is_pressed = 0; ++ ++ /* Now scan the keys and send press events. */ ++ for (i=0; i < 8; i++) { ++ if (keys[i]) { ++ int row; ++ ++ for (row=0; row < 16; row++) { ++ if (keys[i] & (1 << row)) { ++ if (previous_keys[i] & (1 << row)) { ++ /* Generate the hold event. */ ++ handle_scancode((row<<3)+i, 1); ++ } else { ++ /* Generate the down event. */ ++ handle_scancode((row<<3)+i, 1); ++ } ++ ++ key_is_pressed = 1; ++ } ++ } ++ } ++ } ++ ++ /* Update the state variables. */ ++ memcpy(previous_keys, keys, 8 * sizeof(unsigned long)); ++} ++ ++static char edb7211_unexpected_up(unsigned char scancode) ++{ ++ return 0200; ++} ++ ++static void edb7211_leds(unsigned char leds) ++{ ++} ++ ++/* ++ * Initialize the keyboard hardware. Set the column drives low and ++ * start the timer. ++ */ ++void __init ++edb7211_kbd_init_hw(void) ++{ ++ k_translate = edb7211_translate; ++ k_unexpected_up = edb7211_unexpected_up; ++ k_leds = edb7211_leds; ++ ++ /* ++ * If we had the ability to use interrupts, we would want to drive all ++ * columns high. But we have more keys than can generate interrupts, so ++ * we leave them floating. ++ */ ++ clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | KBSC_X, ++ SYSCON1); ++ ++ /* Initialize the matrix processing task. */ ++ kbd_process_task.routine = edb7211_kbd_process; ++ kbd_process_task.data = NULL; ++ ++ /* Setup the timer to poll the keyboard. */ ++ init_timer(&edb7211_kbd_timer); ++ edb7211_kbd_timer.function = edb7211_kbd_timeout; ++ edb7211_kbd_timer.data = (unsigned long)NULL; ++ edb7211_kbd_timer.expires = jiffies + KEYBOARD_SCAN_INTERVAL; ++ add_timer(&edb7211_kbd_timer); ++} ++ ++ +diff -urN linux-2.4.26/drivers/char/epxa_wdt.c linux-2.4.26-vrs1/drivers/char/epxa_wdt.c +--- linux-2.4.26/drivers/char/epxa_wdt.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/epxa_wdt.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,178 @@ ++/* ++ * Watchdog driver for the Altera Excalibur EPXA1DB ++ * ++ * (c) Copyright 2003 Krzysztof Marianski ++ * Based on SA11x0 Watchdog driver by Oleg Drokin ++ * ++ * 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 material is provided "AS-IS" and at no charge ++ * ++ * (c) Copyright 2003 Krzysztof Marianski ++ * ++ * 1/08/2003 Initial release ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define WATCHDOG00_TYPE (volatile unsigned int*) ++#include ++#include ++ ++#define TIMER_MARGIN 30 /* (secs) Default is 30 seconds */ ++ ++static int margin = TIMER_MARGIN; /* in seconds */ ++static int epxa1wdt_users; ++static unsigned char last_written_byte; ++ ++#ifdef CONFIG_WATCHDOG_NOWAYOUT ++static int nowayout=1; ++#else ++static int nowayout=0; ++#endif ++ ++#ifdef MODULE ++MODULE_PARM(margin,"i"); ++MODULE_PARM(nowayout, "i"); ++#endif ++ ++/* ++ * Allow only one person to hold it open ++ */ ++ ++static int epxa1dog_open(struct inode *inode, struct file *file) ++{ ++ if(test_and_set_bit(1,&epxa1wdt_users)) ++ return -EBUSY; ++ ++ /* Reset the Watchdog, just to be sure we don't set ++ a value close to actual value of WDOG_COUNT register */ ++ *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_1; ++ *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_2; ++ ++ /* Activate EPXA1DB Watchdog timer */ ++ *WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE))= (EXC_INPUT_CLK_FREQUENCY * margin) & WDOG_CR_TRIGGER_MSK; ++ ++ last_written_byte = 'V'; //in case user opens it only to ioctl ++ return 0; ++} ++ ++static int epxa1dog_release(struct inode *inode, struct file *file) ++{ ++ /* ++ * Shut off the timer and set lock bit when no special ++ * character 'V' was last written ++ */ ++ ++ if ((last_written_byte != 'V') && (nowayout)) { ++ *WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE)) |= WDOG_CR_LK_MSK; ++ printk("No special character 'V' was written to Watchdog just before closing it\n"); ++ printk("WATCHDOG LOCKED - Reboot expected!!!\n"); ++ } else ++ *WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE))=0; ++ ++ epxa1wdt_users = 0; ++ ++ return 0; ++} ++ ++static ssize_t epxa1dog_write(struct file *file, const char *data, size_t len, loff_t *ppos) ++{ ++ /* Can't seek (pwrite) on this device */ ++ if (ppos != &file->f_pos) ++ return -ESPIPE; ++ ++ /* Reset Watchdog timer. */ ++ if(len) { ++ *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_1; ++ *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_2; ++ last_written_byte = *data; ++ return 1; ++ } ++ return 0; ++} ++ ++static int epxa1dog_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ static struct watchdog_info ident = { ++ identity: "EPXA Watchdog", ++ }; ++ ++ switch(cmd){ ++ default: ++ return -ENOIOCTLCMD; ++ case WDIOC_GETSUPPORT: ++ return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)); ++// case WDIOC_GETSTATUS: //TODO ++// return put_user(0,(int *)arg); ++// case WDIOC_GETBOOTSTATUS: //TODO ++// return 0; ++ case WDIOC_KEEPALIVE: ++ *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_1; ++ *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_2; ++ return 0; ++ case WDIOC_SETTIMEOUT: ++ *WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE))= (EXC_INPUT_CLK_FREQUENCY * margin) & WDOG_CR_TRIGGER_MSK; ++ return 0; ++ case WDIOC_GETTIMEOUT: ++ return put_user( ((*WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE)))/EXC_INPUT_CLK_FREQUENCY), (int*)arg); ++ } ++} ++ ++static struct file_operations epxa1dog_fops = { ++ .owner = THIS_MODULE, ++ .write = epxa1dog_write, ++ .ioctl = epxa1dog_ioctl, ++ .open = epxa1dog_open, ++ .release = epxa1dog_release, ++}; ++ ++static struct miscdevice epxa1dog_miscdev= ++{ ++ .minor = WATCHDOG_MINOR, ++ .name = "EPXA watchdog", ++ .fops = &epxa1dog_fops ++}; ++ ++static int __init epxa1dog_init(void) ++{ ++ int ret; ++ ++ ret = misc_register(&epxa1dog_miscdev); ++ ++ if (ret) ++ return ret; ++ ++ printk("EPXA Watchdog Timer: timer margin %d sec\n", margin); ++ printk("EPXA Watchdog Timer: no way out is %s\n", nowayout ? "enabled" : "disabled"); ++ ++ return 0; ++} ++ ++static void __exit epxa1dog_exit(void) ++{ ++ misc_deregister(&epxa1dog_miscdev); ++} ++ ++module_init(epxa1dog_init); ++module_exit(epxa1dog_exit); ++ ++MODULE_AUTHOR("Krzysztof Marianski "); ++MODULE_DESCRIPTION("EPXA Watchdog Timer"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/char/gc_kbmap.h linux-2.4.26-vrs1/drivers/char/gc_kbmap.h +--- linux-2.4.26/drivers/char/gc_kbmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/gc_kbmap.h 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,162 @@ ++ ++ ++#define KK_NONE 0x7f ++#define KK_ESC 0x00 ++#define KK_F1 0x01 ++#define KK_F2 0x02 ++#define KK_F3 0x03 ++#define KK_F4 0x04 ++#define KK_F5 0x05 ++#define KK_F6 0x06 ++#define KK_F7 0x07 ++#define KK_F8 0x08 ++#define KK_F9 0x09 ++#define KK_F10 0x0a ++#define KK_F11 0x0b ++#define KK_F12 0x0c ++#define KK_PRNT 0x0d ++#define KK_SCRL 0x0e ++#define KK_BRK 0x0f ++#define KK_AGR 0x10 ++#define KK_1 0x11 ++#define KK_2 0x12 ++#define KK_3 0x13 ++#define KK_4 0x14 ++#define KK_5 0x15 ++#define KK_6 0x16 ++#define KK_7 0x17 ++#define KK_8 0x18 ++#define KK_9 0x19 ++#define KK_0 0x1a ++#define KK_MINS 0x1b ++#define KK_EQLS 0x1c ++#define KK_BKSP 0x1e ++#define KK_INS 0x1f ++#define KK_HOME 0x20 ++#define KK_PGUP 0x21 ++#define KK_NUML 0x22 ++#define KP_SLH 0x23 ++#define KP_STR 0x24 ++#define KP_MNS 0x3a ++#define KK_TAB 0x26 ++#define KK_Q 0x27 ++#define KK_W 0x28 ++#define KK_E 0x29 ++#define KK_R 0x2a ++#define KK_T 0x2b ++#define KK_Y 0x2c ++#define KK_U 0x2d ++#define KK_I 0x2e ++#define KK_O 0x2f ++#define KK_P 0x30 ++#define KK_LSBK 0x31 ++#define KK_RSBK 0x32 ++#define KK_ENTR 0x47 ++#define KK_DEL 0x34 ++#define KK_END 0x35 ++#define KK_PGDN 0x36 ++#define KP_7 0x37 ++#define KP_8 0x38 ++#define KP_9 0x39 ++#define KP_PLS 0x4b ++#define KK_CAPS 0x5d ++#define KK_A 0x3c ++#define KK_S 0x3d ++#define KK_D 0x3e ++#define KK_F 0x3f ++#define KK_G 0x40 ++#define KK_H 0x41 ++#define KK_J 0x42 ++#define KK_K 0x43 ++#define KK_L 0x44 ++#define KK_SEMI 0x45 ++#define KK_SQOT 0x46 ++#define KK_HASH 0x1d ++#define KP_4 0x48 ++#define KP_5 0x49 ++#define KP_6 0x4a ++#define KK_LSFT 0x4c ++#define KK_BSLH 0x33 ++#define KK_Z 0x4e ++#define KK_X 0x4f ++#define KK_C 0x50 ++#define KK_V 0x51 ++#define KK_B 0x52 ++#define KK_N 0x53 ++#define KK_M 0x54 ++#define KK_COMA 0x55 ++#define KK_DOT 0x56 ++#define KK_FSLH 0x57 ++#define KK_RSFT 0x58 ++#define KK_UP 0x59 ++#define KP_1 0x5a ++#define KP_2 0x5b ++#define KP_3 0x5c ++#define KP_ENT 0x67 ++#define KK_LCTL 0x3b ++#define KK_LALT 0x5e ++#define KK_SPCE 0x5f ++#define KK_RALT 0x60 ++#define KK_RCTL 0x61 ++#define KK_LEFT 0x62 ++#define KK_DOWN 0x63 ++#define KK_RGHT 0x64 ++#define KP_0 0x65 ++#define KP_DOT 0x66 ++ ++static char kbmap[128] = { ++KK_NONE, KK_LALT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_AGR, KK_BSLH, KK_TAB, KK_Z, KK_A, KK_X, KK_NONE, ++KK_NONE, KK_NONE, KK_LSFT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_LCTL, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, 0x21, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_ESC, KK_DEL, KK_Q, KK_CAPS, KK_S, KK_C, KK_3, ++KK_NONE, KK_1, KK_NONE, KK_W, KK_NONE, KK_D, KK_V, KK_4, ++KK_NONE, KK_2, KK_T, KK_E, KK_NONE, KK_F, KK_B, KK_5, ++KK_NONE, KK_9, KK_Y, KK_R, KK_K, KK_G, KK_N, KK_6, ++KK_NONE, KK_0, KK_U, KK_O, KK_L, KK_H, KK_M, KK_7, ++KK_NONE, KK_MINS, KK_I, KK_P, KK_SEMI, KK_J, KK_COMA, KK_8, ++KK_NONE, KK_EQLS, KK_ENTR, KK_LSBK, KK_BSLH, KK_FSLH, KK_DOT, KK_NONE, ++KK_NONE, KK_NONE, KK_RSFT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_BKSP, KK_DOWN, KK_RSBK, KK_UP, KK_LEFT, KK_SPCE, KK_RGHT, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE}; ++ ++static char kbmapFN[128] = { ++KK_NONE, KK_LALT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_LSFT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_LCTL, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, 0x21, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F3, ++KK_NONE, KK_F1, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F4, ++KK_NONE, KK_F2, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F5, ++KK_NONE, KK_F9, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F6, ++KK_NONE, KK_F10, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F7, ++KK_NONE, KK_NUML, KK_NONE, KK_INS, KK_PRNT, KK_NONE, KK_NONE, KK_F8, ++KK_NONE, KK_BRK, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_RSFT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_PGDN, KK_SCRL, KK_PGUP, KK_HOME, KK_NONE, KK_END, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE}; ++ ++static char kbmapNL[128] = { ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KP_9, KK_NONE, KK_NONE, KP_2, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KP_STR, KP_4, KP_6, KP_3, KK_NONE, KP_0, KP_7, ++KK_NONE, KK_NONE, KP_5, KP_MNS, KP_PLS, KP_1, KK_NONE, KP_8, ++KK_NONE, KK_NONE, KP_ENT, KK_NONE, KK_NONE, KP_SLH, KP_DOT, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, ++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE}; ++ ++ ++ +diff -urN linux-2.4.26/drivers/char/gc_keyb.c linux-2.4.26-vrs1/drivers/char/gc_keyb.c +--- linux-2.4.26/drivers/char/gc_keyb.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/gc_keyb.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,1145 @@ ++/* ++ * linux/arch/arm/drivers/char/gc_keyb.c ++ * ++ * Copyright 2000 Applied Data Systems ++ * ++ * Keyboard & Smartio driver for GraphicsClient ARM Linux. ++ * Graphics Client is SA1110 based single board computer by ++ * Applied Data Systems (http://www.applieddata.net) ++ * ++ * Change log: ++ * 7-10/6/01 Thomas Thaele ++ * - Added Keyboard Sniffer on /dev/sio12 ++ * - First implementation of PC- compatible Scancodes (thanks to pc_keyb.c) ++ * 3/23/01 Woojung Huh ++ * Power Management added ++ * 12/01/00 Woojung Huh ++ * Bug fixed ++ * 11/16/00 Woojung Huh [whuh@applieddata.net] ++ * Added smartio device driver on it ++ */ ++ ++/* ++ * Introduced setkeycode, ketkeycode for the GC+ by Thomas Thaele ++ * GC+ now performs like a real PC on the keyboard. ++ * Warning: this code is still beta! PrntScrn and Pause keys are not ++ * completely tested and implemented!!! Keyboard driver can be confused ++ * by hacking like crazy on the keyboard. (hardware problem on serial line?) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ADS_AVR_IRQ 63 ++ ++#define SMARTIO_IOCTL_BASES 's' ++#define SMARTIO_KPD_TIMEOUT _IOW(SMARTIO_IOCTL_BASES, 0, int) ++#define SMARTIO_KPD_SETUP _IOW(SMARTIO_IOCTL_BASES, 1, short) ++#define SMARTIO_BL_CONTROL _IOW(SMARTIO_IOCTL_BASES, 2, char) ++#define SMARTIO_BL_CONTRAST _IOW(SMARTIO_IOCTL_BASES, 3, char) ++#define SMARTIO_PORT_CONFIG _IOW(SMARTIO_IOCTL_BASES, 4, char) ++#define SMARTIO_SNIFFER_TIMEOUT _IOW(SMARTIO_IOCTL_BASES, 5, long) ++ ++ ++/* Simple translation table for the SysRq keys */ ++ ++#ifdef CONFIG_MAGIC_SYSRQ ++unsigned char pckbd_sysrq_xlate[128] = ++ "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ ++ "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ ++ "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ ++ "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ ++ "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ ++ "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ ++ "\r\000/"; /* 0x60 - 0x6f */ ++#endif ++ ++/* ++ * Translation of escaped scancodes to keycodes. ++ * This is now user-settable. ++ * The keycodes 1-88,96-111,119 are fairly standard, and ++ * should probably not be changed - changing might confuse X. ++ * X also interprets scancode 0x5d (KEY_Begin). ++ * ++ * For 1-88 keycode equals scancode. ++ */ ++ ++#define E0_KPENTER 96 ++#define E0_RCTRL 97 ++#define E0_KPSLASH 98 ++#define E0_PRSCR 99 ++#define E0_RALT 100 ++#define E0_BREAK 101 /* (control-pause) */ ++#define E0_HOME 102 ++#define E0_UP 103 ++#define E0_PGUP 104 ++#define E0_LEFT 105 ++#define E0_RIGHT 106 ++#define E0_END 107 ++#define E0_DOWN 108 ++#define E0_PGDN 109 ++#define E0_INS 110 ++#define E0_DEL 111 ++ ++#define E1_PAUSE 119 ++ ++/* ++ * The keycodes below are randomly located in 89-95,112-118,120-127. ++ * They could be thrown away (and all occurrences below replaced by 0), ++ * but that would force many users to use the `setkeycodes' utility, where ++ * they needed not before. It does not matter that there are duplicates, as ++ * long as no duplication occurs for any single keyboard. ++ */ ++#define SC_LIM 89 ++ ++#define FOCUS_PF1 85 /* actual code! */ ++#define FOCUS_PF2 89 ++#define FOCUS_PF3 90 ++#define FOCUS_PF4 91 ++#define FOCUS_PF5 92 ++#define FOCUS_PF6 93 ++#define FOCUS_PF7 94 ++#define FOCUS_PF8 95 ++#define FOCUS_PF9 120 ++#define FOCUS_PF10 121 ++#define FOCUS_PF11 122 ++#define FOCUS_PF12 123 ++ ++#define JAP_86 124 ++/* tfj@olivia.ping.dk: ++ * The four keys are located over the numeric keypad, and are ++ * labelled A1-A4. It's an rc930 keyboard, from ++ * Regnecentralen/RC International, Now ICL. ++ * Scancodes: 59, 5a, 5b, 5c. ++ */ ++#define RGN1 124 ++#define RGN2 125 ++#define RGN3 126 ++#define RGN4 127 ++ ++static unsigned char high_keys[128 - SC_LIM] = { ++ RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ ++ 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ ++ 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ ++ FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ ++ FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ ++}; ++ ++/* BTC */ ++#define E0_MACRO 112 ++/* LK450 */ ++#define E0_F13 113 ++#define E0_F14 114 ++#define E0_HELP 115 ++#define E0_DO 116 ++#define E0_F17 117 ++#define E0_KPMINPLUS 118 ++/* ++ * My OmniKey generates e0 4c for the "OMNI" key and the ++ * right alt key does nada. [kkoller@nyx10.cs.du.edu] ++ */ ++#define E0_OK 124 ++/* ++ * New microsoft keyboard is rumoured to have ++ * e0 5b (left window button), e0 5c (right window button), ++ * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] ++ * [or: Windows_L, Windows_R, TaskMan] ++ */ ++#define E0_MSLW 125 ++#define E0_MSRW 126 ++#define E0_MSTM 127 ++ ++static unsigned char e0_keys[128] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ ++ 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ ++ 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ ++ E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ ++ E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ ++ E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ ++ E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ ++ 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ ++ 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ ++ 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ ++}; ++ ++int gc_kbd_setkeycode(unsigned int scancode, unsigned int keycode) ++{ ++ if (scancode < SC_LIM || scancode > 255 || keycode > 127) ++ return -EINVAL; ++ if (scancode < 128) ++ high_keys[scancode - SC_LIM] = keycode; ++ else ++ e0_keys[scancode - 128] = keycode; ++ return 0; ++} ++ ++int gc_kbd_getkeycode(unsigned int scancode) ++{ ++ return ++ (scancode < SC_LIM || scancode > 255) ? -EINVAL : ++ (scancode < 128) ? high_keys[scancode - SC_LIM] : ++ e0_keys[scancode - 128]; ++} ++ ++int gc_kbd_translate(unsigned char scancode, unsigned char *keycode, ++ char raw_mode) ++{ ++ static int prev_scancode; ++ ++ /* special prefix scancodes.. */ ++ if (scancode == 0xe0 || scancode == 0xe1) { ++ prev_scancode = scancode; ++ return 0; ++ } ++ ++ /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */ ++ if (scancode == 0x00 || scancode == 0xff) { ++ prev_scancode = 0; ++ return 0; ++ } ++ ++ scancode &= 0x7f; ++ ++ if (prev_scancode) { ++ /* ++ * usually it will be 0xe0, but a Pause key generates ++ * e1 1d 45 e1 9d c5 when pressed, and nothing when released ++ */ ++ if (prev_scancode != 0xe0) { ++ if (prev_scancode == 0xe1 && scancode == 0x1d) { ++ prev_scancode = 0x100; ++ return 0; ++ } else if (prev_scancode == 0x100 && scancode == 0x45) { ++ *keycode = E1_PAUSE; ++ prev_scancode = 0; ++ } else { ++#ifdef KBD_REPORT_UNKN ++ if (!raw_mode) ++ printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); ++#endif ++ prev_scancode = 0; ++ return 0; ++ } ++ } else { ++ prev_scancode = 0; ++ /* ++ * The keyboard maintains its own internal caps lock and ++ * num lock statuses. In caps lock mode E0 AA precedes make ++ * code and E0 2A follows break code. In num lock mode, ++ * E0 2A precedes make code and E0 AA follows break code. ++ * We do our own book-keeping, so we will just ignore these. ++ */ ++ /* ++ * For my keyboard there is no caps lock mode, but there are ++ * both Shift-L and Shift-R modes. The former mode generates ++ * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. ++ * So, we should also ignore the latter. - aeb@cwi.nl ++ */ ++ if (scancode == 0x2a || scancode == 0x36) ++ return 0; ++ ++ if (e0_keys[scancode]) ++ *keycode = e0_keys[scancode]; ++ else { ++#ifdef KBD_REPORT_UNKN ++ if (!raw_mode) ++ printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", ++ scancode); ++#endif ++ return 0; ++ } ++ } ++ } else if (scancode >= SC_LIM) { ++ /* This happens with the FOCUS 9000 keyboard ++ Its keys PF1..PF12 are reported to generate ++ 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f ++ Moreover, unless repeated, they do not generate ++ key-down events, so we have to zero up_flag below */ ++ /* Also, Japanese 86/106 keyboards are reported to ++ generate 0x73 and 0x7d for \ - and \ | respectively. */ ++ /* Also, some Brazilian keyboard is reported to produce ++ 0x73 and 0x7e for \ ? and KP-dot, respectively. */ ++ ++ *keycode = high_keys[scancode - SC_LIM]; ++ ++ if (!*keycode) { ++ if (!raw_mode) { ++#ifdef KBD_REPORT_UNKN ++ printk(KERN_INFO "keyboard: unrecognized scancode (%02x)" ++ " - ignored\n", scancode); ++#endif ++ } ++ return 0; ++ } ++ } else ++ *keycode = scancode; ++ return 1; ++} ++ ++// this table converts the hardware dependent codes of a MF-2 Keyboard to ++// the codes normally comming out of a i8042. This table is 128 Bytes too ++// big, but for stability reasons it should be kept like it is! ++// There is no range checking in the code! ++static int mf_two_kbdmap[256] = { ++ 00, 67, 65, 63, 61, 59, 60, 88, 00, 68, 66, 64, 62, 15, 41, 00, ++ 00, 56, 42, 00, 29, 16, 02, 00, 00, 00, 44, 31, 30, 17, 03, 00, ++ 00, 46, 45, 32, 18, 05, 04, 00, 00, 57, 47, 33, 20, 19, 06, 00, ++ 00, 49, 48, 35, 34, 21, 7, 00, 00, 00, 50, 36, 22, 8, 9, 00, ++ 00, 51, 37, 23, 24, 11, 10, 00, 00, 52, 53, 38, 39, 25, 12, 00, ++ 00, 00, 40, 00, 26, 13, 00, 00, 58, 54, 28, 27, 00, 43, 00, 00, ++ 00, 86, 00, 00, 00, 00, 14, 00, 00, 79, 00, 75, 71, 00, 00, 00, ++ 82, 83, 80, 76, 77, 72, 01, 69, 87, 78, 81, 74, 55, 73, 70, 00, ++ 00, 00, 00, 65, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ++ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ++ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ++ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ++ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ++ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ++ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, ++ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 }; ++ ++ ++// some texts displayed by the proc_file_system ++static char *kbd_sniff[2] = { "off", "on" }; ++static char *kbd_sniff_mode[2] = { "passive", "active" }; ++ ++#define PASSIVE 0 ++#define ACTIVE 1 ++ ++// is the sniffer active (1) or inactive (0) ++static int SNIFFER = 0; ++// do we get a copy (SNIFFMODE = PASSIVE) or do we get the original data (SNIFFMODE = ACTIVE) ++// and have to reinsert the data ++static int SNIFFMODE = PASSIVE; ++ ++// we allow only one process to sniff ++static int sniffer_in_use = 0; ++ ++// timeout for the keyboard sniffer -1 = blocking, otherwise timeout in msecs ++static long sniffer_timeout = -1; ++ ++// the value we sniffed from the keyboard ++static int sniffed_value; ++ ++static char *smartio_version = "1.02 MF-II compatibility patch "; ++static char *smartio_date = "Aug-27-2001"; ++ ++static int sio_reset_flag; ++static int kbd_press_flag; ++ ++static void send_SSP_msg(unchar *pBuf, int num) ++{ ++ ushort tmp; ++ int i; ++ ++ for (i=0;i 7)) ++ return 0xFFFF; ++ ++ CONV_ADC_CMD.Opt[0] = (unchar) channel; ++ ++ lock_smartio(&flags); ++ send_SSP_msg((unchar *) &CONV_ADC_CMD, 3); ++ unlock_smartio(&flags); ++ ++ interruptible_sleep_on(&smartio_adc_queue); ++ ++ return adc_value & 0x3FF; ++} ++ ++static ushort read_sio_port(int port) ++{ ++ unsigned long flags; ++ ushort ret; ++ ++ if ((port < SMARTIO_PORT_B) || (port > SMARTIO_PORT_D)) ++ return 0xFFFF; ++ ++ READ_PORT_CMD.Code = (unchar) port; ++ ++ lock_smartio(&flags); ++ send_SSP_msg((unchar *) &READ_PORT_CMD, 2); ++ ret = read_SSP_response(1); ++ unlock_smartio(&flags); ++ ++ return ret; ++} ++ ++static ushort read_sio_kpd(void) ++{ ++ long timeout; ++ ++ // kpd_timeout is mSec order ++ // interrupt_sleep_on_timeout is based on 10msec timer tick ++ if (kpd_timeout == -1) { ++ interruptible_sleep_on(&smartio_kpd_queue); ++ } ++ else { ++ timeout = interruptible_sleep_on_timeout(&smartio_kpd_queue, ++ kpd_timeout/10); ++ if (timeout == 0) { ++ // timeout without keypad input ++ return 0xFFFF; ++ } ++ } ++ return kpd_value; ++} ++ ++static ushort read_sio_sniff(void) ++{ ++ long timeout; ++ ++ // kpd_timeout is mSec order ++ // interrupt_sleep_on_timeout is based on 10msec timer tick ++ if (sniffer_timeout == -1) { ++ interruptible_sleep_on(&sniffer_queue); ++ } ++ else { ++ timeout = interruptible_sleep_on_timeout(&sniffer_queue, ++ sniffer_timeout/10); ++ if (timeout == 0) { ++ // timeout without keypad input ++ return -1; ++ } ++ } ++ return (ushort)sniffed_value; ++} ++ ++static struct sio_ver { ++ uint DevVer; ++ uint DevType; ++ uint FwLevel; ++}; ++ ++static ushort read_sio_version(struct sio_ver *ptr) ++{ ++ unsigned long flags; ++ ushort ret; ++ ++ // Read Device Version ++ lock_smartio(&flags); ++ send_SSP_msg((unchar *) &READ_DEVVER_CMD, 2); ++ ret = read_SSP_response(1); ++ unlock_smartio(&flags); ++ ptr->DevVer = (uint)ret; ++ // Read Device Type ++ lock_smartio(&flags); ++ send_SSP_msg((unchar *) &READ_DEVTYPE_CMD, 2); ++ ret = read_SSP_response(2); ++ unlock_smartio(&flags); ++ // swap MSB & LSB ++ ret = ((ret & 0xFF) << 8) | ((ret & 0xFF00) >> 8); ++ ptr->DevType = (uint)ret; ++ // Read Firmware Level ++ lock_smartio(&flags); ++ send_SSP_msg((unchar *) &READ_FWLEVEL_CMD, 2); ++ ret = read_SSP_response(2); ++ unlock_smartio(&flags); ++ // swap MSB & LSB ++ ret = ((ret & 0xFF) << 8) | ((ret & 0xFF00) >> 8); ++ ptr->FwLevel = (uint)ret; ++ ++ return 0; ++} ++ ++static ssize_t sio_read(struct file *file, char *buf, size_t count, loff_t *ppos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ unsigned int minor = MINOR(inode->i_rdev); ++ ushort *ret = (ushort *)buf; ++ ++ switch (minor) { ++ case SMARTIO_ADC: ++ if ((*ret = read_sio_adc(buf[0])) != 0xFFFF) ++ return sizeof(ushort); // 2 bytes ++ case SMARTIO_PORT_B: ++ case SMARTIO_PORT_C: ++ case SMARTIO_PORT_D: ++ if ((*ret = read_sio_port(minor)) != 0xFFFF) ++ return sizeof(ushort); ++ case SMARTIO_VERSION: ++ if ((read_sio_version((struct sio_ver *)buf)) != 0xFFFF) ++ return sizeof(struct sio_ver); ++ case SMARTIO_KEYPAD: ++ if ((*ret = read_sio_kpd()) != 0xFFFF) ++ return sizeof(ushort); ++ case SMARTIO_KBD_SNIFFER: ++ if ((*ret = read_sio_sniff()) != (ushort)-1) ++ return 1; ++ default : ++ return -ENXIO; ++ } ++} ++ ++static SMARTIO_CMD WRITE_PORT_CMD = { 0x81, 0x00, { 0x00, 0x00 } }; ++static SMARTIO_CMD SELECT_OPT_CMD = { 0x80, 0x00, { 0x00, 0x00 } }; ++static SMARTIO_CMD CONTROL_BL_CMD = { 0x80, 0x00, { 0x00, 0x00 } }; ++static SMARTIO_CMD CONTRAST_BL_CMD = { 0x80, 0x21, { 0x00, 0x00 } }; ++static SMARTIO_CMD CONTROL_KPD_CMD = { 0x80, 0x27, { 0x00, 0x00 } }; ++static SMARTIO_CMD CONTROL_VEE_CMD = { 0x80, 0x22, { 0x00, 0x00 } }; ++ ++static ushort write_sio_port(int port, unchar value) ++{ ++ unsigned long flags; ++ ++ if ((port < SMARTIO_PORT_B) || (port > SMARTIO_PORT_D)) ++ return 0xFFFF; ++ ++ WRITE_PORT_CMD.Code = (unchar) port; ++ WRITE_PORT_CMD.Opt[0] = (unchar) value; ++ ++ lock_smartio(&flags); ++ send_SSP_msg((unchar *) &WRITE_PORT_CMD, 3); ++ unlock_smartio(&flags); ++ ++ return 0; ++} ++ ++static ushort write_sio_select(unchar select) ++{ ++ unsigned long flags; ++ ++ if ((select < 1) || (select > 2)) ++ return 0xFFFF; ++ ++ SELECT_OPT_CMD.Code = (unchar) (select + 0x28); ++ ++ lock_smartio(&flags); ++ send_SSP_msg((unchar *) &SELECT_OPT_CMD, 2); ++ unlock_smartio(&flags); ++ ++ return 0; ++} ++ ++static ushort control_sio_backlite(int cmd, int value) ++{ ++ unsigned long flags; ++ ++ if (cmd == SMARTIO_BL_CONTRAST) { ++ value &= 0xFF; ++ CONTRAST_BL_CMD.Opt[0] = (unchar) value; ++ ++ lock_smartio(&flags); ++ send_SSP_msg((unchar *) &CONTRAST_BL_CMD, 3); ++ unlock_smartio(&flags); ++ } ++ else if (cmd == SMARTIO_BL_CONTROL) { ++ if (value == 0x00) { ++ // Backlite OFF ++ CONTROL_BL_CMD.Code = 0x24; ++ } ++ else { ++ // Backlite ON ++ CONTROL_BL_CMD.Code = 0x23; ++ } ++ lock_smartio(&flags); ++ send_SSP_msg((unchar *) &CONTROL_BL_CMD, 2); ++ unlock_smartio(&flags); ++ } ++ else ++ return 0xFFFF; ++ ++ return 0; ++} ++ ++static ushort control_sio_keypad(int x, int y) ++{ ++ unsigned long flags; ++ ++ if ( (x<1) || (x>8) || (y<1) || (y>8)) { ++ return 0xFFFF; ++ } ++ ++ CONTROL_KPD_CMD.Opt[0] = (unchar) x; ++ CONTROL_KPD_CMD.Opt[1] = (unchar) y; ++ ++ lock_smartio(&flags); ++ send_SSP_msg((unchar *) &CONTROL_KPD_CMD, 4); ++ unlock_smartio(&flags); ++ ++ return 0; ++} ++ ++static ushort control_sio_vee(int value) ++{ ++ unsigned long flags; ++ ++ value &= 0xFF; ++ CONTROL_VEE_CMD.Opt[0] = (unchar) value; ++ ++ lock_smartio(&flags); ++ send_SSP_msg((unchar *) &CONTROL_VEE_CMD, 3); ++ unlock_smartio(&flags); ++ ++ return 0; ++} ++ ++static ssize_t sio_write(struct file *file, const char *buf, size_t cont, loff_t *ppos) ++{ ++ struct inode *inode = file->f_dentry->d_inode; ++ unsigned int minor = MINOR(inode->i_rdev); ++ ++ switch (minor) { ++ case SMARTIO_PORT_B: ++ case SMARTIO_PORT_C: ++ case SMARTIO_PORT_D: ++ if (write_sio_port(minor, buf[0]) != 0xFFFF) ++ return 1; ++ case SMARTIO_SELECT_OPTION: ++ if (write_sio_select(buf[0]) != 0xFFFF) ++ return 1; ++ case SMARTIO_BACKLITE: ++ if (control_sio_backlite(SMARTIO_BL_CONTROL, buf[0]) != 0xFFFF) ++ return 1; ++ case SMARTIO_KEYPAD: ++ if (control_sio_keypad(buf[0], buf[1]) != 0xFFFF) ++ return 2; ++ case SMARTIO_VEE_PWM: ++ if (control_sio_vee(buf[0]) != 0xFFFF) ++ return 1; ++ case SMARTIO_KBD_SNIFFER: ++ // here are the scancodes injected ++ handle_scancode((unchar)buf[0], (buf[0] & 0x80) ? 0 : 1); ++ wake_up_interruptible(&keyboard_done_queue); ++ // give some time to process! File IO is a bit faster than manual typing ;-) ++ udelay(10000); ++ return 1; ++ default: ++ return -ENXIO; ++ } ++} ++ ++static unsigned int sio_poll(struct file *file, struct poll_table_struct *wait) ++{ ++ return 0; ++} ++ ++static SMARTIO_CMD IOCTL_PORT_CMD = { 0x81, 0x00, { 0x00, 0x00 } }; ++ ++static ushort ioctl_sio_port(int port, unchar value) ++{ ++ unsigned long flags; ++ ++ if ((port < SMARTIO_PORT_B) || (port > SMARTIO_PORT_D)) ++ return 0xFFFF; ++ ++ IOCTL_PORT_CMD.Code = (unchar) port + 0x04; // 0x05 ~ 0x08 ++ if (port == SMARTIO_PORT_B) { ++ // Port B has 4 bits only ++ IOCTL_PORT_CMD.Opt[0] = (unchar) value & 0x0F; ++ } ++ else ++ IOCTL_PORT_CMD.Opt[0] = (unchar) value; ++ ++ lock_smartio(&flags); ++ send_SSP_msg((unchar *) &IOCTL_PORT_CMD, 3); ++ unlock_smartio(&flags); ++ ++ return 0; ++} ++ ++static int sio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ unsigned int minor = MINOR(inode->i_rdev); ++ unchar *buf = (unchar *)arg; ++ ++ switch (minor) { ++ case SMARTIO_PORT_B: ++ case SMARTIO_PORT_C: ++ case SMARTIO_PORT_D: ++ if (cmd == SMARTIO_PORT_CONFIG) { ++ if (ioctl_sio_port(minor, buf[0]) != 0xFFFF) ++ return 0; ++ } ++ return -EINVAL; ++ case SMARTIO_SELECT_OPTION: ++ if (write_sio_select(buf[0]) != 0xFFFF) return 0; ++ return -EINVAL; ++ case SMARTIO_BACKLITE: ++ if (cmd == SMARTIO_BL_CONTROL) { ++ if (control_sio_backlite(SMARTIO_BL_CONTROL, buf[0]) != 0xFFFF) return 0; ++ } ++ else if (cmd == SMARTIO_BL_CONTRAST) { ++ if (control_sio_backlite(SMARTIO_BL_CONTRAST, buf[0]) != 0xFFFF) return 0; ++ } ++ else return -EINVAL; ++ case SMARTIO_KEYPAD: ++ if (cmd == SMARTIO_KPD_TIMEOUT) { ++ kpd_timeout = *(long*)buf; ++ return 0; ++ } ++ else if (cmd == SMARTIO_KPD_SETUP) { ++ if (control_sio_keypad(buf[0], buf[1]) != 0xFFFF) return 0; ++ } ++ return -EINVAL; ++ case SMARTIO_VEE_PWM: ++ if (control_sio_vee(buf[0]) != 0xFFFF) return 0; ++ return -EINVAL; ++ case SMARTIO_KBD_SNIFFER: ++ if (cmd == SMARTIO_SNIFFER_TIMEOUT) { ++ sniffer_timeout = *(long*)buf; ++ if (sniffer_timeout < 0) sniffer_timeout = -1; ++ // the value will be devided by 10 later on ++ if (!sniffer_timeout) sniffer_timeout = 10; ++ return 0; ++ } ++ return -EINVAL; ++ default: ++ return -ENXIO; ++ } ++} ++ ++static int sio_open(struct inode *inode, struct file *file) ++{ ++ unsigned int minor = MINOR(inode->i_rdev); ++ ++ // we open all by default. we only have a special handler for the kbd sniffer ++ switch (minor) { ++ case SMARTIO_KBD_SNIFFER: ++ if (sniffer_in_use) return -EBUSY; ++ sniffer_in_use = 1; ++ SNIFFER = 1; ++ // sniff in active or passive mode ++ if ((file->f_flags & O_RDWR) == O_RDWR) SNIFFMODE = 1; else SNIFFMODE = 0; ++ // do we have a blocking or non blocking sniffer? ++ if ((file->f_flags & O_NONBLOCK) == O_NONBLOCK) sniffer_timeout = 100; else sniffer_timeout = -1; ++ break; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++static int sio_close(struct inode *inode, struct file *file) ++{ ++ unsigned int minor = MINOR(inode->i_rdev); ++ ++ switch (minor) { ++ case SMARTIO_KBD_SNIFFER: ++ SNIFFER = 0; ++ SNIFFMODE = 0; ++ sniffer_in_use = 0; ++ break; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++static struct file_operations sio_fops = { ++ read: sio_read, ++ write: sio_write, ++ poll: sio_poll, ++ ioctl: sio_ioctl, ++ open: sio_open, ++ release: sio_close, ++}; ++ ++static struct proc_dir_entry *sio_dir, *parent_dir = NULL; ++ ++#define SMARTIO_MAJOR 58 ++#define MAJOR_NR SMARTIO_MAJOR ++ ++#define PROC_NAME "sio" ++ ++static int sio_read_proc(char *buf, char **start, off_t pos, int count, int *eof, void *data) ++{ ++ char *p = buf; ++ ++ p += sprintf(p, "ADS SMARTIO Status: \n"); ++ p += sprintf(p, "\t Keyboard Interrupt : %lu\n", kbd_int); ++ p += sprintf(p, "\t Keypad Interrupt : %lu\n", kpd_int); ++ p += sprintf(p, "\t ADC Interrupt : %lu\n", adc_int); ++ p += sprintf(p, "\t Keyboard Sniffer : %s mode : %s\n", kbd_sniff[ SNIFFER ], kbd_sniff_mode [ SNIFFMODE ]); ++ ++ return (p-buf); ++} ++ ++#ifdef CONFIG_PM ++static int pm_smartio_callback(struct pm_dev *dev, pm_request_t rqst, void *data) ++{ ++ switch (rqst) { ++ case PM_RESUME: ++ gc_sio_init(); ++ break; ++ case PM_SUSPEND: ++ // 4/5/01 Woojung ++ // It checks Keybard received pair of press/release code. ++ // System can sleep before receiving release code ++ if (kbd_press_flag) { ++ interruptible_sleep_on(&keyboard_done_queue); ++ } ++ break; ++ } ++ ++ return 0; ++} ++#endif ++ ++void __init sio_init(void) ++{ ++ if (register_chrdev(MAJOR_NR, "sio", &sio_fops)) { ++ printk("smartio : unable to get major %d\n", MAJOR_NR); ++ return; ++ } ++ else { ++ printk("smartio driver initialized. version %s, date:%s\n", ++ smartio_version, smartio_date); ++ ++ if (sio_reset_flag != 1) { ++ gc_sio_init(); ++ if (request_irq(ADS_AVR_IRQ, gc_sio_interrupt,0,"sio",NULL) != 0){ ++ printk("smartio : Could not allocate IRQ!\n"); ++ return; ++ } ++ } ++ ++ if ((sio_dir = create_proc_entry(PROC_NAME, 0, parent_dir)) == NULL) { ++ printk("smartio : Unable to create /proc entry\n"); ++ return; ++ } ++ else { ++ sio_dir->read_proc = sio_read_proc; ++#ifdef CONFIG_PM ++ pm_register(PM_SYS_DEV, PM_SYS_KBC, pm_smartio_callback); ++#endif ++ } ++ } ++} +diff -urN linux-2.4.26/drivers/char/gckeymap.c linux-2.4.26-vrs1/drivers/char/gckeymap.c +--- linux-2.4.26/drivers/char/gckeymap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/gckeymap.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,262 @@ ++/* Do not edit this file! It was automatically generated by */ ++/* loadkeys --mktable defkeymap.map > defkeymap.c */ ++ ++#include ++#include ++#include ++ ++u_short plain_map[NR_KEYS] = { ++ 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, ++ 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, ++ 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, ++ 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, ++ 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, ++ 0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, ++ 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, ++ 0xf703, 0xf020, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, ++ 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, ++ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, ++ 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, ++ 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, ++ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, ++ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, ++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++}; ++ ++u_short shift_map[NR_KEYS] = { ++ 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, ++ 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, ++ 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, ++ 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, ++ 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, ++ 0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, ++ 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, ++ 0xf703, 0xf020, 0xf207, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e, ++ 0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf213, 0xf203, 0xf307, ++ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, ++ 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a, ++ 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++ 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, ++ 0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, ++ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, ++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++}; ++ ++u_short altgr_map[NR_KEYS] = { ++ 0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, ++ 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200, ++ 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, ++ 0xfb6f, 0xfb70, 0xf200, 0xf07e, 0xf201, 0xf702, 0xf914, 0xfb73, ++ 0xf917, 0xf919, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf200, ++ 0xf200, 0xf200, 0xf700, 0xf200, 0xfb7a, 0xfb78, 0xf916, 0xfb76, ++ 0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, ++ 0xf703, 0xf200, 0xf207, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, ++ 0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf202, 0xf911, ++ 0xf912, 0xf913, 0xf30b, 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf90b, ++ 0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, ++ 0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++ 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, ++ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, ++ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, ++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++}; ++ ++u_short ctrl_map[NR_KEYS] = { ++ 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, ++ 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200, ++ 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, ++ 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, 0xf702, 0xf001, 0xf013, ++ 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, ++ 0xf007, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, ++ 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, ++ 0xf703, 0xf000, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, ++ 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf204, 0xf307, ++ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, ++ 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf10a, ++ 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, ++ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, ++ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, ++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++}; ++ ++u_short shift_ctrl_map[NR_KEYS] = { ++ 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200, ++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, ++ 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, ++ 0xf00f, 0xf010, 0xf200, 0xf200, 0xf201, 0xf702, 0xf001, 0xf013, ++ 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, ++ 0xf200, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016, ++ 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, ++ 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf208, 0xf200, 0xf307, ++ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, ++ 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200, ++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++ 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, ++ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, ++ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, ++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++}; ++ ++u_short alt_map[NR_KEYS] = { ++ 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, ++ 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, ++ 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, ++ 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, ++ 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, ++ 0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, ++ 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, ++ 0xf703, 0xf820, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, ++ 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf907, ++ 0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, ++ 0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, ++ 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, ++ 0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, ++ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, ++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++}; ++ ++u_short ctrl_alt_map[NR_KEYS] = { ++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++ 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, ++ 0xf80f, 0xf810, 0xf200, 0xf200, 0xf201, 0xf702, 0xf801, 0xf813, ++ 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, ++ 0xf200, 0xf200, 0xf700, 0xf200, 0xf81a, 0xf818, 0xf803, 0xf816, ++ 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, ++ 0xf703, 0xf200, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, ++ 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf200, 0xf307, ++ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, ++ 0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf200, 0xf50a, ++ 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++ 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, ++ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, ++ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, ++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, ++}; ++ ++ushort *key_maps[MAX_NR_KEYMAPS] = { ++ plain_map, shift_map, altgr_map, 0, ++ ctrl_map, shift_ctrl_map, 0, 0, ++ alt_map, 0, 0, 0, ++ ctrl_alt_map, 0 ++}; ++ ++unsigned int keymap_count = 7; ++ ++/* ++ * Philosophy: most people do not define more strings, but they who do ++ * often want quite a lot of string space. So, we statically allocate ++ * the default and allocate dynamically in chunks of 512 bytes. ++ */ ++ ++char func_buf[] = { ++ '\033', '[', '[', 'A', 0, ++ '\033', '[', '[', 'B', 0, ++ '\033', '[', '[', 'C', 0, ++ '\033', '[', '[', 'D', 0, ++ '\033', '[', '[', 'E', 0, ++ '\033', '[', '1', '7', '~', 0, ++ '\033', '[', '1', '8', '~', 0, ++ '\033', '[', '1', '9', '~', 0, ++ '\033', '[', '2', '0', '~', 0, ++ '\033', '[', '2', '1', '~', 0, ++ '\033', '[', '2', '3', '~', 0, ++ '\033', '[', '2', '4', '~', 0, ++ '\033', '[', '2', '5', '~', 0, ++ '\033', '[', '2', '6', '~', 0, ++ '\033', '[', '2', '8', '~', 0, ++ '\033', '[', '2', '9', '~', 0, ++ '\033', '[', '3', '1', '~', 0, ++ '\033', '[', '3', '2', '~', 0, ++ '\033', '[', '3', '3', '~', 0, ++ '\033', '[', '3', '4', '~', 0, ++ '\033', '[', '1', '~', 0, ++ '\033', '[', '2', '~', 0, ++ '\033', '[', '3', '~', 0, ++ '\033', '[', '4', '~', 0, ++ '\033', '[', '5', '~', 0, ++ '\033', '[', '6', '~', 0, ++ '\033', '[', 'M', 0, ++ '\033', '[', 'P', 0, ++}; ++ ++char *funcbufptr = func_buf; ++int funcbufsize = sizeof(func_buf); ++int funcbufleft = 0; /* space left */ ++ ++char *func_table[MAX_NR_FUNC] = { ++ func_buf + 0, ++ func_buf + 5, ++ func_buf + 10, ++ func_buf + 15, ++ func_buf + 20, ++ func_buf + 25, ++ func_buf + 31, ++ func_buf + 37, ++ func_buf + 43, ++ func_buf + 49, ++ func_buf + 55, ++ func_buf + 61, ++ func_buf + 67, ++ func_buf + 73, ++ func_buf + 79, ++ func_buf + 85, ++ func_buf + 91, ++ func_buf + 97, ++ func_buf + 103, ++ func_buf + 109, ++ func_buf + 115, ++ func_buf + 120, ++ func_buf + 125, ++ func_buf + 130, ++ func_buf + 135, ++ func_buf + 140, ++ func_buf + 145, ++ 0, ++ 0, ++ func_buf + 149, ++ 0, ++}; ++ ++struct kbdiacr accent_table[MAX_DIACR] = { ++ {'`', 'A', '\300'}, {'`', 'a', '\340'}, ++ {'\'', 'A', '\301'}, {'\'', 'a', '\341'}, ++ {'^', 'A', '\302'}, {'^', 'a', '\342'}, ++ {'~', 'A', '\303'}, {'~', 'a', '\343'}, ++ {'"', 'A', '\304'}, {'"', 'a', '\344'}, ++ {'O', 'A', '\305'}, {'o', 'a', '\345'}, ++ {'0', 'A', '\305'}, {'0', 'a', '\345'}, ++ {'A', 'A', '\305'}, {'a', 'a', '\345'}, ++ {'A', 'E', '\306'}, {'a', 'e', '\346'}, ++ {',', 'C', '\307'}, {',', 'c', '\347'}, ++ {'`', 'E', '\310'}, {'`', 'e', '\350'}, ++ {'\'', 'E', '\311'}, {'\'', 'e', '\351'}, ++ {'^', 'E', '\312'}, {'^', 'e', '\352'}, ++ {'"', 'E', '\313'}, {'"', 'e', '\353'}, ++ {'`', 'I', '\314'}, {'`', 'i', '\354'}, ++ {'\'', 'I', '\315'}, {'\'', 'i', '\355'}, ++ {'^', 'I', '\316'}, {'^', 'i', '\356'}, ++ {'"', 'I', '\317'}, {'"', 'i', '\357'}, ++ {'-', 'D', '\320'}, {'-', 'd', '\360'}, ++ {'~', 'N', '\321'}, {'~', 'n', '\361'}, ++ {'`', 'O', '\322'}, {'`', 'o', '\362'}, ++ {'\'', 'O', '\323'}, {'\'', 'o', '\363'}, ++ {'^', 'O', '\324'}, {'^', 'o', '\364'}, ++ {'~', 'O', '\325'}, {'~', 'o', '\365'}, ++ {'"', 'O', '\326'}, {'"', 'o', '\366'}, ++ {'/', 'O', '\330'}, {'/', 'o', '\370'}, ++ {'`', 'U', '\331'}, {'`', 'u', '\371'}, ++ {'\'', 'U', '\332'}, {'\'', 'u', '\372'}, ++ {'^', 'U', '\333'}, {'^', 'u', '\373'}, ++ {'"', 'U', '\334'}, {'"', 'u', '\374'}, ++ {'\'', 'Y', '\335'}, {'\'', 'y', '\375'}, ++ {'T', 'H', '\336'}, {'t', 'h', '\376'}, ++ {'s', 's', '\337'}, {'"', 'y', '\377'}, ++ {'s', 'z', '\337'}, {'i', 'j', '\377'}, ++}; ++ ++unsigned int accent_table_size = 68; +diff -urN linux-2.4.26/drivers/char/gckeymap.map linux-2.4.26-vrs1/drivers/char/gckeymap.map +--- linux-2.4.26/drivers/char/gckeymap.map 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/gckeymap.map 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,357 @@ ++# Default kernel keymap. This uses 7 modifier combinations. ++keymaps 0-2,4-5,8,12 ++# Change the above line into ++# keymaps 0-2,4-6,8,12 ++# in case you want the entries ++# altgr control keycode 83 = Boot ++# altgr control keycode 111 = Boot ++# below. ++# ++# In fact AltGr is used very little, and one more keymap can ++# be saved by mapping AltGr to Alt (and adapting a few entries): ++# keycode 100 = Alt ++# ++keycode 1 = Escape Escape ++ alt keycode 1 = Meta_Escape ++keycode 2 = one exclam ++ alt keycode 2 = Meta_one ++keycode 3 = two at at ++ control keycode 3 = nul ++ shift control keycode 3 = nul ++ alt keycode 3 = Meta_two ++keycode 4 = three numbersign ++ control keycode 4 = Escape ++ alt keycode 4 = Meta_three ++keycode 5 = four dollar dollar ++ control keycode 5 = Control_backslash ++ alt keycode 5 = Meta_four ++keycode 6 = five percent ++ control keycode 6 = Control_bracketright ++ alt keycode 6 = Meta_five ++keycode 7 = six asciicircum ++ control keycode 7 = Control_asciicircum ++ alt keycode 7 = Meta_six ++keycode 8 = seven ampersand braceleft ++ control keycode 8 = Control_underscore ++ alt keycode 8 = Meta_seven ++keycode 9 = eight asterisk bracketleft ++ control keycode 9 = Delete ++ alt keycode 9 = Meta_eight ++keycode 10 = nine parenleft bracketright ++ alt keycode 10 = Meta_nine ++keycode 11 = zero parenright braceright ++ alt keycode 11 = Meta_zero ++keycode 12 = minus underscore backslash ++ control keycode 12 = Control_underscore ++ shift control keycode 12 = Control_underscore ++ alt keycode 12 = Meta_minus ++keycode 13 = equal plus ++ alt keycode 13 = Meta_equal ++keycode 14 = Delete Delete ++ control keycode 14 = BackSpace ++ alt keycode 14 = Meta_Delete ++keycode 15 = Tab Tab ++ alt keycode 15 = Meta_Tab ++keycode 16 = q ++keycode 17 = w ++keycode 18 = e ++ altgr keycode 18 = Hex_E ++keycode 19 = r ++keycode 20 = t ++keycode 21 = y ++keycode 22 = u ++keycode 23 = i ++keycode 24 = o ++keycode 25 = p ++keycode 26 = bracketleft braceleft ++ control keycode 26 = Escape ++ alt keycode 26 = Meta_bracketleft ++keycode 27 = bracketright braceright asciitilde ++ control keycode 27 = Control_bracketright ++ alt keycode 27 = Meta_bracketright ++keycode 28 = Return ++ alt keycode 28 = Meta_Control_m ++keycode 29 = Control ++keycode 30 = a ++ altgr keycode 30 = Hex_A ++keycode 31 = s ++keycode 32 = d ++ altgr keycode 32 = Hex_D ++keycode 33 = f ++ altgr keycode 33 = Hex_F ++keycode 34 = g ++keycode 35 = h ++keycode 36 = j ++keycode 37 = k ++keycode 38 = l ++keycode 39 = semicolon colon ++ alt keycode 39 = Meta_semicolon ++keycode 40 = apostrophe quotedbl ++ control keycode 40 = Control_g ++ alt keycode 40 = Meta_apostrophe ++keycode 41 = grave asciitilde ++ control keycode 41 = nul ++ alt keycode 41 = Meta_grave ++keycode 42 = Shift ++keycode 43 = backslash bar ++ control keycode 43 = Control_backslash ++ alt keycode 43 = Meta_backslash ++keycode 44 = z ++keycode 45 = x ++keycode 46 = c ++ altgr keycode 46 = Hex_C ++keycode 47 = v ++keycode 48 = b ++ altgr keycode 48 = Hex_B ++keycode 49 = n ++keycode 50 = m ++keycode 51 = comma less ++ alt keycode 51 = Meta_comma ++keycode 52 = period greater ++ control keycode 52 = Compose ++ alt keycode 52 = Meta_period ++keycode 53 = slash question ++ control keycode 53 = Delete ++ alt keycode 53 = Meta_slash ++keycode 54 = Shift ++keycode 55 = KP_Multiply ++keycode 56 = Alt ++keycode 57 = space space ++ control keycode 57 = nul ++ alt keycode 57 = Meta_space ++keycode 58 = Caps_Lock ++keycode 59 = F1 F11 Console_13 ++ control keycode 59 = F1 ++ alt keycode 59 = Console_1 ++ control alt keycode 59 = Console_1 ++keycode 60 = F2 F12 Console_14 ++ control keycode 60 = F2 ++ alt keycode 60 = Console_2 ++ control alt keycode 60 = Console_2 ++keycode 61 = F3 F13 Console_15 ++ control keycode 61 = F3 ++ alt keycode 61 = Console_3 ++ control alt keycode 61 = Console_3 ++keycode 62 = F4 F14 Console_16 ++ control keycode 62 = F4 ++ alt keycode 62 = Console_4 ++ control alt keycode 62 = Console_4 ++keycode 63 = F5 F15 Console_17 ++ control keycode 63 = F5 ++ alt keycode 63 = Console_5 ++ control alt keycode 63 = Console_5 ++keycode 64 = F6 F16 Console_18 ++ control keycode 64 = F6 ++ alt keycode 64 = Console_6 ++ control alt keycode 64 = Console_6 ++keycode 65 = F7 F17 Console_19 ++ control keycode 65 = F7 ++ alt keycode 65 = Console_7 ++ control alt keycode 65 = Console_7 ++keycode 66 = F8 F18 Console_20 ++ control keycode 66 = F8 ++ alt keycode 66 = Console_8 ++ control alt keycode 66 = Console_8 ++keycode 67 = F9 F19 Console_21 ++ control keycode 67 = F9 ++ alt keycode 67 = Console_9 ++ control alt keycode 67 = Console_9 ++keycode 68 = F10 F20 Console_22 ++ control keycode 68 = F10 ++ alt keycode 68 = Console_10 ++ control alt keycode 68 = Console_10 ++keycode 69 = Num_Lock ++ shift keycode 69 = Bare_Num_Lock ++keycode 70 = Scroll_Lock Show_Memory Show_Registers ++ control keycode 70 = Show_State ++ alt keycode 70 = Scroll_Lock ++keycode 71 = KP_7 ++ alt keycode 71 = Ascii_7 ++ altgr keycode 71 = Hex_7 ++keycode 72 = KP_8 ++ alt keycode 72 = Ascii_8 ++ altgr keycode 72 = Hex_8 ++keycode 73 = KP_9 ++ alt keycode 73 = Ascii_9 ++ altgr keycode 73 = Hex_9 ++keycode 74 = KP_Subtract ++keycode 75 = KP_4 ++ alt keycode 75 = Ascii_4 ++ altgr keycode 75 = Hex_4 ++keycode 76 = KP_5 ++ alt keycode 76 = Ascii_5 ++ altgr keycode 76 = Hex_5 ++keycode 77 = KP_6 ++ alt keycode 77 = Ascii_6 ++ altgr keycode 77 = Hex_6 ++keycode 78 = KP_Add ++keycode 79 = KP_1 ++ alt keycode 79 = Ascii_1 ++ altgr keycode 79 = Hex_1 ++keycode 80 = KP_2 ++ alt keycode 80 = Ascii_2 ++ altgr keycode 80 = Hex_2 ++keycode 81 = KP_3 ++ alt keycode 81 = Ascii_3 ++ altgr keycode 81 = Hex_3 ++keycode 82 = KP_0 ++ alt keycode 82 = Ascii_0 ++ altgr keycode 82 = Hex_0 ++keycode 83 = KP_Period ++# altgr control keycode 83 = Boot ++ control alt keycode 83 = Boot ++keycode 84 = Last_Console ++keycode 85 = ++keycode 86 = less greater bar ++ alt keycode 86 = Meta_less ++keycode 87 = F11 F11 Console_23 ++ control keycode 87 = F11 ++ alt keycode 87 = Console_11 ++ control alt keycode 87 = Console_11 ++keycode 88 = F12 F12 Console_24 ++ control keycode 88 = F12 ++ alt keycode 88 = Console_12 ++ control alt keycode 88 = Console_12 ++keycode 89 = ++keycode 90 = ++keycode 91 = ++keycode 92 = ++keycode 93 = ++keycode 94 = ++keycode 95 = ++keycode 96 = KP_Enter ++keycode 97 = Control ++keycode 98 = KP_Divide ++keycode 99 = Control_backslash ++ control keycode 99 = Control_backslash ++ alt keycode 99 = Control_backslash ++keycode 100 = AltGr ++keycode 101 = Break ++keycode 102 = Find ++keycode 103 = Up ++keycode 104 = Prior ++ shift keycode 104 = Scroll_Backward ++keycode 105 = Left ++ alt keycode 105 = Decr_Console ++keycode 106 = Right ++ alt keycode 106 = Incr_Console ++keycode 107 = Select ++keycode 108 = Down ++keycode 109 = Next ++ shift keycode 109 = Scroll_Forward ++keycode 110 = Insert ++keycode 111 = Remove ++# altgr control keycode 111 = Boot ++ control alt keycode 111 = Boot ++keycode 112 = Macro ++keycode 113 = F13 ++keycode 114 = F14 ++keycode 115 = Help ++keycode 116 = Do ++keycode 117 = F17 ++keycode 118 = KP_MinPlus ++keycode 119 = Pause ++keycode 120 = ++keycode 121 = ++keycode 122 = ++keycode 123 = ++keycode 124 = ++keycode 125 = ++keycode 126 = ++keycode 127 = ++string F1 = "\033[[A" ++string F2 = "\033[[B" ++string F3 = "\033[[C" ++string F4 = "\033[[D" ++string F5 = "\033[[E" ++string F6 = "\033[17~" ++string F7 = "\033[18~" ++string F8 = "\033[19~" ++string F9 = "\033[20~" ++string F10 = "\033[21~" ++string F11 = "\033[23~" ++string F12 = "\033[24~" ++string F13 = "\033[25~" ++string F14 = "\033[26~" ++string F15 = "\033[28~" ++string F16 = "\033[29~" ++string F17 = "\033[31~" ++string F18 = "\033[32~" ++string F19 = "\033[33~" ++string F20 = "\033[34~" ++string Find = "\033[1~" ++string Insert = "\033[2~" ++string Remove = "\033[3~" ++string Select = "\033[4~" ++string Prior = "\033[5~" ++string Next = "\033[6~" ++string Macro = "\033[M" ++string Pause = "\033[P" ++compose '`' 'A' to 'ƀ' ++compose '`' 'a' to 'Ć ' ++compose '\'' 'A' to 'Ɓ' ++compose '\'' 'a' to 'Ć”' ++compose '^' 'A' to 'Ƃ' ++compose '^' 'a' to 'Ć¢' ++compose '~' 'A' to 'ƃ' ++compose '~' 'a' to 'Ć£' ++compose '"' 'A' to 'Ƅ' ++compose '"' 'a' to 'Ƥ' ++compose 'O' 'A' to 'ƅ' ++compose 'o' 'a' to 'Ć„' ++compose '0' 'A' to 'ƅ' ++compose '0' 'a' to 'Ć„' ++compose 'A' 'A' to 'ƅ' ++compose 'a' 'a' to 'Ć„' ++compose 'A' 'E' to 'Ɔ' ++compose 'a' 'e' to 'Ʀ' ++compose ',' 'C' to 'Ƈ' ++compose ',' 'c' to 'Ƨ' ++compose '`' 'E' to 'ƈ' ++compose '`' 'e' to 'ĆØ' ++compose '\'' 'E' to 'Ɖ' ++compose '\'' 'e' to 'Ć©' ++compose '^' 'E' to 'Ɗ' ++compose '^' 'e' to 'ĆŖ' ++compose '"' 'E' to 'Ƌ' ++compose '"' 'e' to 'Ć«' ++compose '`' 'I' to 'ƌ' ++compose '`' 'i' to 'Ƭ' ++compose '\'' 'I' to 'ƍ' ++compose '\'' 'i' to 'Ć­' ++compose '^' 'I' to 'Ǝ' ++compose '^' 'i' to 'Ć®' ++compose '"' 'I' to 'Ə' ++compose '"' 'i' to 'ĆÆ' ++compose '-' 'D' to 'Ɛ' ++compose '-' 'd' to 'Ć°' ++compose '~' 'N' to 'Ƒ' ++compose '~' 'n' to 'Ʊ' ++compose '`' 'O' to 'ƒ' ++compose '`' 'o' to 'Ć²' ++compose '\'' 'O' to 'Ɠ' ++compose '\'' 'o' to 'Ć³' ++compose '^' 'O' to 'Ɣ' ++compose '^' 'o' to 'Ć“' ++compose '~' 'O' to 'ƕ' ++compose '~' 'o' to 'Ƶ' ++compose '"' 'O' to 'Ɩ' ++compose '"' 'o' to 'ƶ' ++compose '/' 'O' to 'Ƙ' ++compose '/' 'o' to 'Ćø' ++compose '`' 'U' to 'ƙ' ++compose '`' 'u' to 'Ć¹' ++compose '\'' 'U' to 'ƚ' ++compose '\'' 'u' to 'Ćŗ' ++compose '^' 'U' to 'ƛ' ++compose '^' 'u' to 'Ć»' ++compose '"' 'U' to 'Ɯ' ++compose '"' 'u' to 'Ć¼' ++compose '\'' 'Y' to 'Ɲ' ++compose '\'' 'y' to 'Ć½' ++compose 'T' 'H' to 'ƞ' ++compose 't' 'h' to 'Ć¾' ++compose 's' 's' to 'Ɵ' ++compose '"' 'y' to 'Ćæ' ++compose 's' 'z' to 'Ɵ' ++compose 'i' 'j' to 'Ćæ' +diff -urN linux-2.4.26/drivers/char/generic_serial.c linux-2.4.26-vrs1/drivers/char/generic_serial.c +--- linux-2.4.26/drivers/char/generic_serial.c 2002-11-28 23:53:12.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/char/generic_serial.c 2004-01-14 21:32:25.000000000 +0000 +@@ -883,6 +883,9 @@ + if(!memcmp(tiosp->c_cc, old_termios->c_cc, NCC)) printk("c_cc changed\n"); + } + ++ /* ++ * should be using tty_get_baud_rate() here -- rmk ++ */ + baudrate = tiosp->c_cflag & CBAUD; + if (baudrate & CBAUDEX) { + baudrate &= ~CBAUDEX; +@@ -957,6 +960,11 @@ + unsigned long flags; + unsigned long page; + ++ /* ++ * Do we expect to allocate tmp_buf from an interrupt routine? ++ * If not, then save_flags() cli() and restore_flags() are ++ * redundant here and should be replaced by a semaphore. -- rmk ++ */ + save_flags (flags); + if (!tmp_buf) { + page = get_free_page(GFP_KERNEL); +@@ -976,6 +984,11 @@ + if (port->flags & ASYNC_INITIALIZED) + return 0; + ++ /* ++ * Do we expect to allocate xmit_buf from an interrupt routine? ++ * If not, then save_flags() cli() and restore_flags() are ++ * redundant here and should be replaced by a semaphore. -- rmk ++ */ + if (!port->xmit_buf) { + /* We may sleep in get_free_page() */ + unsigned long tmp; +@@ -1016,7 +1029,7 @@ + struct serial_struct sio; + + if (copy_from_user(&sio, sp, sizeof(struct serial_struct))) +- return(-EFAULT); ++ return -EFAULT; + + if (!capable(CAP_SYS_ADMIN)) { + if ((sio.baud_base != port->baud_base) || +diff -urN linux-2.4.26/drivers/char/keyboard.c linux-2.4.26-vrs1/drivers/char/keyboard.c +--- linux-2.4.26/drivers/char/keyboard.c 2003-11-28 18:26:20.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/char/keyboard.c 2004-02-11 01:12:05.000000000 +0000 +@@ -14,13 +14,17 @@ + * `Sticky' modifier keys, 951006. + * + * 11-11-96: SAK should now work in the raw mode (Martin Mares) +- * ++ * + * Modified to provide 'generic' keyboard support by Hamish Macdonald + * Merge with the m68k keyboard driver and split-off of the PC low-level + * parts by Geert Uytterhoeven, May 1997 + * + * 27-05-97: Added support for the Magic SysRq Key (Martin Mares) + * 30-07-98: Dead keys redone, aeb@cwi.nl. ++ * ++ * 04-04-1998: Added keyboard autorepeat support (some keyboards don't ++ * autorepeat, and some keyboard changers interfere with keyboard ++ * autorepeat settings). - Russell King (rmk@arm.linux.org.uk) + */ + + #include +@@ -30,6 +34,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -61,6 +66,14 @@ + #define KBD_DEFLOCK 0 + #endif + ++/* ++ * Default autorepeat settings. ++ * DEFAULT_REPEAT_TIMEOUT is the timeout from the keypress to the first repeat ++ * DEFAULT_REPEAT_INTERVAL is the timeout between successive repeats ++ */ ++#define DEFAULT_REPEAT_TIMEOUT HZ*300/1000 ++#define DEFAULT_REPEAT_INTERVAL HZ*30/1000 ++ + void (*kbd_ledfunc)(unsigned int led); + EXPORT_SYMBOL(handle_scancode); + EXPORT_SYMBOL(kbd_ledfunc); +@@ -82,21 +95,25 @@ + static unsigned long key_down[256/BITS_PER_LONG]; + + static int dead_key_next; +-/* ++/* + * In order to retrieve the shift_state (for the mouse server), either +- * the variable must be global, or a new procedure must be created to ++ * the variable must be global, or a new procedure must be created to + * return the value. I chose the former way. + */ + int shift_state; + static int npadch = -1; /* -1 or number assembled on pad */ + static unsigned char diacr; + static char rep; /* flag telling character repeat */ ++static int kbd_repeatkeycode= -1; ++static int kbd_repeattimeout = DEFAULT_REPEAT_TIMEOUT; ++static int kbd_repeatinterval= DEFAULT_REPEAT_INTERVAL; + struct kbd_struct kbd_table[MAX_NR_CONSOLES]; + static struct tty_struct **ttytab; + static struct kbd_struct * kbd = kbd_table; + static struct tty_struct * tty; + static unsigned char prev_scancode; + ++static void kbd_processkeycode(unsigned char scancode, char up_flag, int autorepeat); + void compute_shiftstate(void); + + typedef void (*k_hand)(unsigned char value, char up_flag); +@@ -163,7 +180,8 @@ + * string, and in both cases we might assume that it is + * in utf-8 already. + */ +-void to_utf8(ushort c) { ++void to_utf8(ushort c) ++{ + if (c < 0x80) + put_queue(c); /* 0******* */ + else if (c < 0x800) { +@@ -182,17 +200,23 @@ + * Translation of escaped scancodes to keycodes. + * This is now user-settable (for machines were it makes sense). + */ +- + int setkeycode(unsigned int scancode, unsigned int keycode) + { +- return kbd_setkeycode(scancode, keycode); ++ return kbd_setkeycode(scancode, keycode); + } + + int getkeycode(unsigned int scancode) + { +- return kbd_getkeycode(scancode); ++ return kbd_getkeycode(scancode); + } + ++static void key_callback(unsigned long nr); ++ ++static struct timer_list key_autorepeat_timer = ++{ ++ function: key_callback ++}; ++ + void handle_scancode(unsigned char scancode, int down) + { + unsigned char keycode; +@@ -201,6 +225,7 @@ + char have_keycode; + + pm_access(pm_kbd); ++ + add_keyboard_randomness(scancode | up_flag); + + tty = ttytab? ttytab[fg_console]: NULL; +@@ -223,15 +248,15 @@ + if (raw_mode) { + /* + * The following is a workaround for hardware +- * which sometimes send the key release event twice ++ * which sometimes send the key release event twice + */ + unsigned char next_scancode = scancode|up_flag; + if (have_keycode && up_flag && next_scancode==prev_scancode) { + /* unexpected 2nd release event */ + } else { +- /* ++ /* + * Only save previous scancode if it was a key-up +- * and had a single-byte scancode. ++ * and had a single-byte scancode. + */ + if (!have_keycode) + prev_scancode = 1; +@@ -256,12 +281,35 @@ + * return the keycode if in MEDIUMRAW mode. + */ + ++ kbd_processkeycode(keycode, up_flag, 0); ++ ++out: ++ do_poke_blanked_console = 1; ++ schedule_console_callback(); ++} ++ ++static void ++kbd_processkeycode(unsigned char keycode, char up_flag, int autorepeat) ++{ ++ char raw_mode = (kbd->kbdmode == VC_RAW); ++ + if (up_flag) { + rep = 0; + if(!test_and_clear_bit(keycode, key_down)) + up_flag = kbd_unexpected_up(keycode); +- } else ++ } else { + rep = test_and_set_bit(keycode, key_down); ++ /* If the keyboard autorepeated for us, ignore it. ++ * We do our own autorepeat processing. ++ */ ++ if (rep && !autorepeat) ++ return; ++ } ++ ++ if (kbd_repeatkeycode == keycode || !up_flag || raw_mode) { ++ kbd_repeatkeycode = -1; ++ del_timer(&key_autorepeat_timer); ++ } + + #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ + if (keycode == SYSRQ_KEY) { +@@ -275,6 +323,23 @@ + } + #endif + ++ /* ++ * Calculate the next time when we have to do some autorepeat ++ * processing. Note that we do not do autorepeat processing ++ * while in raw mode but we do do autorepeat processing in ++ * medium raw mode. ++ */ ++ if (!up_flag && !raw_mode) { ++ kbd_repeatkeycode = keycode; ++ if (vc_kbd_mode(kbd, VC_REPEAT)) { ++ if (rep) ++ key_autorepeat_timer.expires = jiffies + kbd_repeatinterval; ++ else ++ key_autorepeat_timer.expires = jiffies + kbd_repeattimeout; ++ add_timer(&key_autorepeat_timer); ++ } ++ } ++ + if (kbd->kbdmode == VC_MEDIUMRAW) { + /* soon keycodes will require more than one byte */ + put_queue(keycode + up_flag); +@@ -343,9 +408,24 @@ + #endif + } + } ++ rep = 0; + out: +- do_poke_blanked_console = 1; +- schedule_console_callback(); ++} ++ ++/* ++ * This clears the key down arrays when the keyboard is reset. On ++ * keyboard reset, this must be called before any keycodes are ++ * received. ++ */ ++void kbd_reset_kdown(void) ++{ ++ int i; ++ ++ for (i = 0; i < NR_SHIFT; i++) ++ k_down[i] = 0; ++ for (i = 0; i < SIZE(key_down); i++) ++ key_down[i] = 0; ++ shift_state = 0; + } + + void put_queue(int ch) +@@ -453,7 +533,7 @@ + static void decr_console(void) + { + int i; +- ++ + for (i = fg_console-1; i != fg_console; i--) { + if (i == -1) + i = MAX_NR_CONSOLES-1; +@@ -531,7 +611,7 @@ + { + } + +-static void do_null() ++static void do_null(void) + { + compute_shiftstate(); + } +@@ -646,8 +726,8 @@ + + static void do_pad(unsigned char value, char up_flag) + { +- static const char *pad_chars = "0123456789+-*/\015,.?()"; +- static const char *app_map = "pqrstuvwxylSRQMnnmPQ"; ++ static const char *pad_chars = "0123456789+-*/\015,.?()#"; ++ static const char *app_map = "pqrstuvwxylSRQMnnmPQS"; + + if (up_flag) + return; /* no action, if this is a key release */ +@@ -748,9 +828,10 @@ + } + } + +-/* called after returning from RAW mode or when changing consoles - +- recompute k_down[] and shift_state from key_down[] */ +-/* maybe called when keymap is undefined, so that shiftkey release is seen */ ++/* Called after returning from RAW mode or when changing consoles - ++ * recompute k_down[] and shift_state from key_down[] ++ * Maybe called when keymap is undefined so that shift key release is seen ++ */ + void compute_shiftstate(void) + { + int i, j, k, sym, val; +@@ -829,19 +910,22 @@ + } + + /* +- * The leds display either (i) the status of NumLock, CapsLock, ScrollLock, +- * or (ii) whatever pattern of lights people want to show using KDSETLED, +- * or (iii) specified bits of specified words in kernel memory. ++ * The leds display either ++ * (i) the status of NumLock, CapsLock, ScrollLock, or ++ * (ii) whatever pattern of lights people want to show using KDSETLED, or ++ * (iii) specified bits of specified words in kernel memory. + */ + + static unsigned char ledstate = 0xff; /* undefined */ + static unsigned char ledioctl; + +-unsigned char getledstate(void) { ++unsigned char getledstate(void) ++{ + return ledstate; + } + +-void setledstate(struct kbd_struct *kbd, unsigned int led) { ++void setledstate(struct kbd_struct *kbd, unsigned int led) ++{ + if (!(led & ~7)) { + ledioctl = led; + kbd->ledmode = LED_SHOW_IOCTL; +@@ -857,7 +941,8 @@ + } ledptrs[3]; + + void register_leds(int console, unsigned int led, +- unsigned int *addr, unsigned int mask) { ++ unsigned int *addr, unsigned int mask) ++{ + struct kbd_struct *kbd = kbd_table + console; + if (led < 3) { + ledptrs[led].addr = addr; +@@ -868,7 +953,8 @@ + kbd->ledmode = LED_SHOW_FLAGS; + } + +-static inline unsigned char getleds(void){ ++static inline unsigned char getleds(void) ++{ + struct kbd_struct *kbd = kbd_table + fg_console; + unsigned char leds; + +@@ -915,6 +1001,19 @@ + { + unsigned char leds = getleds(); + ++ if (rep && kbd_repeatkeycode != -1) { ++ tty = ttytab? ttytab[fg_console]: NULL; ++ kbd = kbd_table + fg_console; ++ ++ /* This prevents the kbd_key routine from being called ++ * twice, once by this BH, and once by the interrupt ++ * routine. ++ */ ++ kbd_disable_irq(); ++ kbd_processkeycode(kbd_repeatkeycode, 0, 1); ++ kbd_enable_irq(); ++ } ++ + if (leds != ledstate) { + ledstate = leds; + kbd_leds(leds); +@@ -937,6 +1036,12 @@ + tasklet_enable(&keyboard_tasklet); + } + ++static void key_callback(unsigned long nr) ++{ ++ rep = 1; ++ tasklet_schedule(&keyboard_tasklet); ++} ++ + typedef void (pm_kbd_func) (void); + + pm_callback pm_kbd_request_override = NULL; +@@ -953,7 +1058,7 @@ + kbd0.slockstate = 0; + kbd0.modeflags = KBD_DEFMODE; + kbd0.kbdmode = VC_XLATE; +- ++ + for (i = 0 ; i < MAX_NR_CONSOLES ; i++) + kbd_table[i] = kbd0; + +@@ -963,7 +1068,7 @@ + + tasklet_enable(&keyboard_tasklet); + tasklet_schedule(&keyboard_tasklet); +- ++ + pm_kbd = pm_register(PM_SYS_DEV, PM_SYS_KBC, pm_kbd_request_override); + + return 0; +diff -urN linux-2.4.26/drivers/char/mem.c linux-2.4.26-vrs1/drivers/char/mem.c +--- linux-2.4.26/drivers/char/mem.c 2003-11-28 18:26:20.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/char/mem.c 2004-01-14 21:39:01.000000000 +0000 +@@ -27,9 +27,6 @@ + #include + #include + +-#ifdef CONFIG_I2C +-extern int i2c_init_all(void); +-#endif + #ifdef CONFIG_FB + extern void fbmem_init(void); + #endif +@@ -740,9 +737,6 @@ + printk("unable to get major %d for memory devs\n", MEM_MAJOR); + memory_devfs_register(); + rand_initialize(); +-#ifdef CONFIG_I2C +- i2c_init_all(); +-#endif + #if defined (CONFIG_FB) + fbmem_init(); + #endif +diff -urN linux-2.4.26/drivers/char/omaha-rtc.c linux-2.4.26-vrs1/drivers/char/omaha-rtc.c +--- linux-2.4.26/drivers/char/omaha-rtc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/omaha-rtc.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,566 @@ ++/* ++ * (C) ARM Limited 2002. ++ * ++ * Real Time Clock interface for Linux on Omaha ++ * ++ * Based on sa1100-rtc.c ++ * ++ * Copyright (c) 2000 Nils Faerber ++ * ++ * Based on rtc.c by Paul Gortmaker ++ * Date/time conversion routines taken from arch/arm/kernel/time.c ++ * by Linus Torvalds and Russell King ++ * and the GNU C Library ++ * ( ... I love the GPL ... just take what you need! ;) ++ * ++ * 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. ++ * ++ * 1.00 2001-06-08 Nicolas Pitre ++ * - added periodic timer capability using OSMR1 ++ * - flag compatibility with other RTC chips ++ * - permission checks for ioctls ++ * - major cleanup, partial rewrite ++ * ++ * 0.03 2001-03-07 CIH ++ * - Modify the bug setups RTC clock. ++ * ++ * 0.02 2001-02-27 Nils Faerber ++ * - removed mktime(), added alarm irq clear ++ * ++ * 0.01 2000-10-01 Nils Faerber ++ * - initial release ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_VERSION "1.00" ++ ++#define epoch 1970 ++ ++#define TIMER_FREQ 3686400 ++ ++#define RTC_DEF_DIVIDER 32768 - 1 ++#define RTC_DEF_TRIM 0 ++ ++/* Those are the bits from a classic RTC we want to mimic */ ++#define RTC_IRQF 0x80 /* any of the following 3 is active */ ++#define RTC_PF 0x40 ++#define RTC_AF 0x20 ++#define RTC_UF 0x10 ++ ++// bitdefs for rtc registers ++#define TICNT_ENABLE 0x80 // Enable tick interrupt ++#define TICNT_PERIOD 0x7F // Divisor required for 1Hz tick ++#define RTC_ENABLE 0x1 // Enable bit for RTC ++ ++static unsigned long rtc_status; ++static unsigned long rtc_irq_data; ++static unsigned long rtc_freq = 1024; ++ ++static struct fasync_struct *rtc_async_queue; ++static DECLARE_WAIT_QUEUE_HEAD(rtc_wait); ++ ++extern spinlock_t rtc_lock; ++ ++static const unsigned char days_in_mo[] = ++ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; ++ ++#define is_leap(year) \ ++ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) ++ ++// all the alarm and rtc registers ++static volatile unsigned int almsec = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMSEC); ++static volatile unsigned int almmin = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMMIN); ++static volatile unsigned int almhour = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMHOUR); ++static volatile unsigned int almday = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMDAY); ++static volatile unsigned int almmon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMMON); ++static volatile unsigned int almyear = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMYEAR); ++ ++static volatile unsigned int bcdsec = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDSEC); ++static volatile unsigned int bcdmin = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDMIN); ++static volatile unsigned int bcdhour = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDHOUR); ++static volatile unsigned int bcdday = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDDAY); ++static volatile unsigned int bcddate = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDDATE); ++static volatile unsigned int bcdmon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDMON); ++static volatile unsigned int bcdyear = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDYEAR); ++ ++/* ++ * Converts seconds since 1970-01-01 00:00:00 to Gregorian date. ++ */ ++ ++static void decodetime (unsigned long t, struct rtc_time *tval) ++{ ++ long days, month, year, rem; ++ ++ days = t / 86400; ++ rem = t % 86400; ++ tval->tm_hour = rem / 3600; ++ rem %= 3600; ++ tval->tm_min = rem / 60; ++ tval->tm_sec = rem % 60; ++ tval->tm_wday = (4 + days) % 7; ++ ++#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) ++ ++ year = epoch; ++ while (days >= (365 + is_leap(year))) { ++ unsigned long yg = year + days / 365; ++ days -= ((yg - year) * 365 ++ + LEAPS_THRU_END_OF (yg - 1) ++ - LEAPS_THRU_END_OF (year - 1)); ++ year = yg; ++ } ++ tval->tm_year = year - 1900; ++ tval->tm_yday = days + 1; ++ ++ month = 0; ++ if (days >= 31) { ++ days -= 31; ++ month++; ++ if (days >= (28 + is_leap(year))) { ++ days -= (28 + is_leap(year)); ++ month++; ++ while (days >= days_in_mo[month]) { ++ days -= days_in_mo[month]; ++ month++; ++ } ++ } ++ } ++ tval->tm_mon = month; ++ tval->tm_mday = days + 1; ++} ++ ++// Get alarm time in seconds ++static unsigned long get_alarm_time(void) ++{ ++ int sec, min,hour,date,mon,year; ++ ++ // Read data from h/w ++ year = __raw_readb(almyear); ++ mon = __raw_readb(almmon); ++ date = __raw_readb(almday); ++ hour = __raw_readb(almhour); ++ min = __raw_readb(almmin); ++ sec = __raw_readb(almsec); ++ ++ // convert all the data into binary ++ year = BCD_TO_BIN(year); ++ mon = BCD_TO_BIN(mon); ++ date = BCD_TO_BIN(date); ++ hour = BCD_TO_BIN(hour); ++ min = BCD_TO_BIN(min); ++ sec = BCD_TO_BIN(sec); ++ ++ // convert year to 19xx or 20xx as appropriate ++ if (year > 69) ++ year += 1900; ++ else ++ year += 2000; ++ ++ // Now calculate number of seconds since time began... ++ return mktime(year,mon,date,hour,min,sec); ++} ++ ++// Get rtc time in seconds ++static unsigned long get_rtc_time(void) ++{ ++ int sec,min,hour,day,date,mon,year; ++ ++ // Read data from h/w ++ year = __raw_readb(bcdyear); ++ mon = __raw_readb(bcdmon); ++ date = __raw_readb(bcdday); ++ day = __raw_readb(bcddate); ++ hour = __raw_readb(bcdhour); ++ min = __raw_readb(bcdmin); ++ sec = __raw_readb(bcdsec); ++ ++ // convert all the data into binary ++ year = BCD_TO_BIN(year); ++ mon = BCD_TO_BIN(mon); ++ date = BCD_TO_BIN(date); ++ day = BCD_TO_BIN(day); ++ hour = BCD_TO_BIN(hour); ++ min = BCD_TO_BIN(min); ++ sec = BCD_TO_BIN(sec); ++ ++ // convert year to 19xx or 20xx as appropriate ++ if (year > 69) ++ year += 1900; ++ else ++ year += 2000; ++ ++ // Now calculate number of seconds since time began... ++ return mktime(year,mon,date,hour,min,sec); ++} ++ ++/* Sets time of alarm */ ++static void set_alarm_time(struct rtc_time *tval) ++{ ++ ++ int sec,min,hour,day,mon,year; ++ ++ // Convert data from binary to 8-bit bcd ++ sec = BIN_TO_BCD(tval->tm_sec); ++ min = BIN_TO_BCD(tval->tm_min); ++ hour = BIN_TO_BCD(tval->tm_hour); ++ day = BIN_TO_BCD(tval->tm_mday); ++ mon = BIN_TO_BCD(tval->tm_mon); ++ ++ // Year is special ++ year = tval->tm_year; ++ if(year > 1999) ++ year -=2000; ++ else ++ year -=1900; ++ ++ year = BIN_TO_BCD(year); ++ ++ // Write all the registers ++ __raw_writeb(year,almyear); ++ __raw_writeb(mon,almmon); ++ __raw_writeb(day,almday); ++ __raw_writeb(hour,almhour); ++ __raw_writeb(min,almmin); ++ __raw_writeb(sec,almsec); ++} ++ ++/* Sets time of alarm */ ++static void set_rtc_time(struct rtc_time *tval) ++{ ++ ++ int sec,min,hour,day,date,mon,year; ++ ++ // Convert data from binary to 8-bit bcd ++ sec = BIN_TO_BCD(tval->tm_sec); ++ min = BIN_TO_BCD(tval->tm_min); ++ hour = BIN_TO_BCD(tval->tm_hour); ++ day = BIN_TO_BCD(tval->tm_mday); ++ date = BIN_TO_BCD(tval->tm_wday); ++ mon = BIN_TO_BCD(tval->tm_mon); ++ ++ // Year is special ++ year = tval->tm_year; ++ if(year > 1999) ++ year -=2000; ++ else ++ year -=1900; ++ ++ year = BIN_TO_BCD(year); ++ ++ // Write all the registers ++ __raw_writeb(year,bcdyear); ++ __raw_writeb(mon,bcdmon); ++ __raw_writeb(date,bcddate); ++ __raw_writeb(day,bcdday); ++ __raw_writeb(hour,bcdhour); ++ __raw_writeb(min,bcdmin); ++ __raw_writeb(sec,bcdsec); ++} ++ ++static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ /* update irq data & counter */ ++ rtc_irq_data += 0x100; ++ ++ /* wake up waiting process */ ++ wake_up_interruptible(&rtc_wait); ++ kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); ++} ++ ++static int rtc_open(struct inode *inode, struct file *file) ++{ ++ if (test_and_set_bit (1, &rtc_status)) ++ return -EBUSY; ++ MOD_INC_USE_COUNT; ++ rtc_irq_data = 0; ++ return 0; ++} ++ ++static int rtc_release(struct inode *inode, struct file *file) ++{ ++ rtc_status = 0; ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++static int rtc_fasync (int fd, struct file *filp, int on) ++{ ++ return fasync_helper (fd, filp, on, &rtc_async_queue); ++} ++ ++static unsigned int rtc_poll(struct file *file, poll_table *wait) ++{ ++ poll_wait (file, &rtc_wait, wait); ++ return (rtc_irq_data) ? 0 : POLLIN | POLLRDNORM; ++} ++ ++static loff_t rtc_llseek(struct file *file, loff_t offset, int origin) ++{ ++ return -ESPIPE; ++} ++ ++ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long data; ++ ssize_t retval; ++ ++ if (count < sizeof(unsigned long)) ++ return -EINVAL; ++ ++ add_wait_queue(&rtc_wait, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ for (;;) { ++ spin_lock_irq (&rtc_lock); ++ data = rtc_irq_data; ++ if (data != 0) { ++ rtc_irq_data = 0; ++ break; ++ } ++ spin_unlock_irq (&rtc_lock); ++ ++ if (file->f_flags & O_NONBLOCK) { ++ retval = -EAGAIN; ++ goto out; ++ } ++ ++ if (signal_pending(current)) { ++ retval = -ERESTARTSYS; ++ goto out; ++ } ++ ++ schedule(); ++ } ++ ++ spin_unlock_irq (&rtc_lock); ++ ++ data -= 0x100; /* the first IRQ wasn't actually missed */ ++ ++ retval = put_user(data, (unsigned long *)buf); ++ if (!retval) ++ retval = sizeof(unsigned long); ++ ++out: ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&rtc_wait, &wait); ++ return retval; ++} ++ ++static int rtc_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ volatile unsigned int rtcalm = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_RTCALM); ++ volatile unsigned int ticnt = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_TICINT); ++ ++ struct rtc_time tm, tm2; ++ switch (cmd) { ++ case RTC_AIE_OFF: ++ spin_lock_irq(&rtc_lock); ++ __raw_writel(0,rtcalm); ++ rtc_irq_data = 0; ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ case RTC_AIE_ON: ++ spin_lock_irq(&rtc_lock); ++ __raw_writel(0x7F,rtcalm); ++ rtc_irq_data = 0; ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ case RTC_UIE_OFF: ++ spin_lock_irq(&rtc_lock); ++ __raw_writel(~TICNT_ENABLE,ticnt); ++ rtc_irq_data = 0; ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ case RTC_UIE_ON: ++ spin_lock_irq(&rtc_lock); ++ __raw_writel(TICNT_ENABLE|TICNT_PERIOD,ticnt); ++ rtc_irq_data = 0; ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ case RTC_PIE_OFF: ++ spin_lock_irq(&rtc_lock); ++ // Periodic int not available ++ rtc_irq_data = 0; ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ case RTC_PIE_ON: ++ spin_lock_irq(&rtc_lock); ++ // Periodic int not available ++ rtc_irq_data = 0; ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ case RTC_ALM_READ: ++ decodetime(get_alarm_time(),&tm); ++ break; ++ case RTC_ALM_SET: ++ if (copy_from_user (&tm2, (struct rtc_time*)arg, sizeof (tm2))) ++ return -EFAULT; ++ decodetime(get_rtc_time(),&tm); ++ if ((unsigned)tm2.tm_hour < 24) ++ tm.tm_hour = tm2.tm_hour; ++ if ((unsigned)tm2.tm_min < 60) ++ tm.tm_min = tm2.tm_min; ++ if ((unsigned)tm2.tm_sec < 60) ++ tm.tm_sec = tm2.tm_sec; ++ ++ // Munge (as per sa1100) ++ tm.tm_year+=1900; ++ tm.tm_mon+=1; ++ ++ // Set the alarm ++ set_alarm_time(&tm); ++ return 0; ++ case RTC_RD_TIME: ++ decodetime (get_rtc_time(), &tm); ++ break; ++ case RTC_SET_TIME: ++ if (!capable(CAP_SYS_TIME)) ++ return -EACCES; ++ if (copy_from_user (&tm, (struct rtc_time*)arg, sizeof (tm))) ++ return -EFAULT; ++ tm.tm_year += 1900; ++ if (tm.tm_year < epoch || (unsigned)tm.tm_mon >= 12 || ++ tm.tm_mday < 1 || tm.tm_mday > (days_in_mo[tm.tm_mon] + ++ (tm.tm_mon == 1 && is_leap(tm.tm_year))) || ++ (unsigned)tm.tm_hour >= 24 || ++ (unsigned)tm.tm_min >= 60 || ++ (unsigned)tm.tm_sec >= 60) ++ return -EINVAL; ++ tm.tm_mon +=1; // wierd: same as sa1100 though (gets month wrong otherwise!) ++ set_rtc_time(&tm); ++ return 0; ++ case RTC_IRQP_READ: ++ return put_user(rtc_freq, (unsigned long *)arg); ++ case RTC_IRQP_SET: ++ if (arg < 1 || arg > TIMER_FREQ) ++ return -EINVAL; ++ if ((arg > 64) && (!capable(CAP_SYS_RESOURCE))) ++ return -EACCES; ++ rtc_freq = arg; ++ return 0; ++ case RTC_EPOCH_READ: ++ return put_user (epoch, (unsigned long *)arg); ++ default: ++ return -EINVAL; ++ } ++ return copy_to_user ((void *)arg, &tm, sizeof (tm)) ? -EFAULT : 0; ++} ++ ++static struct file_operations rtc_fops = { ++ .owner = THIS_MODULE, ++ .llseek = rtc_llseek, ++ .read = rtc_read, ++ .poll = rtc_poll, ++ .ioctl = rtc_ioctl, ++ .open = rtc_open, ++ .release = rtc_release, ++ .fasync = rtc_fasync, ++}; ++ ++static struct miscdevice omahartc_miscdev = { ++ .minor = RTC_MINOR, ++ .name = "rtc", ++ .fops = &rtc_fops, ++}; ++ ++static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) ++{ ++ char *p = page; ++ int len; ++ struct rtc_time tm; ++ ++ decodetime (get_rtc_time(), &tm); ++ p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" ++ "rtc_date\t: %04d-%02d-%02d\n" ++ "rtc_epoch\t: %04d\n", ++ tm.tm_hour, tm.tm_min, tm.tm_sec, ++ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); ++ decodetime (get_alarm_time(), &tm); ++ p += sprintf(p, "alrm_time\t: %02d:%02d:%02d\n" ++ "alrm_date\t: %04d-%02d-%02d\n", ++ tm.tm_hour, tm.tm_min, tm.tm_sec, ++ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); ++ p += sprintf(p, "periodic_freq\t: %ld\n", rtc_freq); ++ ++ len = (p - page) - off; ++ if (len < 0) ++ len = 0; ++ ++ *eof = (len <= count) ? 1 : 0; ++ *start = page + off; ++ ++ return len; ++} ++ ++static int __init rtc_init(void) ++{ ++ volatile unsigned int ticnt = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_TICINT); ++ volatile unsigned int rtccon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_RTCCON); ++ int ret; ++ ++ misc_register (&omahartc_miscdev); ++ create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); ++ ++ // Enable RTC ++ __raw_writel(RTC_ENABLE,rtccon); ++ ++ // Acquire 1Hz timer ++ ret = request_irq (OMAHA_INT_TICK, rtc_interrupt, SA_INTERRUPT, "rtc 1Hz", NULL); ++ if (ret) { ++ printk (KERN_ERR "rtc: IRQ %d already in use.\n", OMAHA_INT_TICK); ++ goto IRQ_TICK_failed; ++ } ++ ++ // Acquire RTC (Alarm interrupt) ++ ret = request_irq (OMAHA_INT_RTC, rtc_interrupt, SA_INTERRUPT, "rtc Alrm", NULL); ++ if (ret) { ++ printk (KERN_ERR "rtc: IRQ %d already in use.\n", OMAHA_INT_RTC); ++ goto IRQ_RTC_failed; ++ } ++ ++ printk (KERN_INFO "Omaha Real Time Clock driver v" DRIVER_VERSION "\n"); ++ ++ // Program tick interrupt divisor to generate real 1Hz clock and enable the interrupt ++ __raw_writeb(TICNT_ENABLE|TICNT_PERIOD,ticnt); ++ ++ return 0; ++ ++IRQ_TICK_failed: ++ free_irq (OMAHA_INT_TICK, NULL); ++IRQ_RTC_failed: ++ free_irq(OMAHA_INT_RTC, NULL); ++ remove_proc_entry ("driver/rtc", NULL); ++ misc_deregister (&omahartc_miscdev); ++ return ret; ++} ++ ++static void __exit rtc_exit(void) ++{ ++ free_irq (OMAHA_INT_TICK, NULL); ++ remove_proc_entry ("driver/rtc", NULL); ++ misc_deregister (&omahartc_miscdev); ++} ++ ++module_init(rtc_init); ++module_exit(rtc_exit); ++ ++MODULE_AUTHOR("ARM Limited "); ++MODULE_DESCRIPTION("Omaha Realtime Clock Driver (RTC)"); ++EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/char/omaha_wdt.c linux-2.4.26-vrs1/drivers/char/omaha_wdt.c +--- linux-2.4.26/drivers/char/omaha_wdt.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/omaha_wdt.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,193 @@ ++/* ++ * Watchdog driver for the Omaha ++ * (C) ARM Limited 2002. ++ * ++ * Based on sa1100_wdt.c ++ * ++ * (c) Copyright 2000 Oleg Drokin ++ * Based on SoftDog driver by Alan Cox ++ * ++ * 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. ++ * ++ * Neither Oleg Drokin nor iXcelerator.com admit liability nor provide ++ * warranty for any of this software. This material is provided ++ * "AS-IS" and at no charge. ++ * ++ * (c) Copyright 2000 Oleg Drokin ++ * ++ * 27/11/2000 Initial release ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TIMER_MARGIN 8 /* (secs) Default is 1 minute */ ++#define WT_TPS 7812 /* Watchdog ticks per second. */ ++#define WT_ENABLE 0x21 // Enable bits for watchdog ++#define WT_CLKSEL_128 0x18 // Select 1/128 divider ++ ++static int omaha_margin = TIMER_MARGIN; /* in seconds */ ++static int omahawdt_users; ++static int pre_margin; ++#ifdef MODULE ++MODULE_PARM(omaha_margin,"i"); ++#endif ++ ++/* ++ * Allow only one person to hold it open ++ */ ++ ++static int omahadog_open(struct inode *inode, struct file *file) ++{ ++ volatile unsigned int wtcon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTCON); ++ volatile unsigned int wtdat = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTDAT); ++ volatile unsigned int wtcnt = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTCNT); ++ unsigned int tmp; ++ ++ if(test_and_set_bit(1,&omahawdt_users)) ++ return -EBUSY; ++ MOD_INC_USE_COUNT; ++ /* Activate omaha Watchdog timer */ ++ ++ /* Assume that uhal has set up pre-scaler according ++ * to the system clock frequency (don't change it!) ++ * ++ * Ie. all counting occurs at 1MHz / 128 = 7812.5Hz ++ * ++ * Since we have 16-bit counter, maximum period is ++ * 65536/7812.5 = 8.338608 seconds! ++ */ ++ ++ pre_margin = WT_TPS * omaha_margin; ++ ++ // Set count to the maximum ++ __raw_writel(pre_margin,wtcnt); ++ ++ // Set the clock division factor ++ tmp = __raw_readl(wtcon); ++ tmp |= WT_CLKSEL_128; ++ __raw_writel(tmp,wtcon); ++ ++ // Program an initial count into WTDAT ++ __raw_writel(0x8000,wtdat); ++ ++ // enable the watchdog ++ tmp = __raw_readl(wtcon); ++ tmp |= WT_ENABLE; ++ ++ __raw_writel(tmp,wtcon); ++ ++ return 0; ++} ++ ++static int omahadog_release(struct inode *inode, struct file *file) ++{ ++ volatile unsigned int wtcon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTCON); ++ unsigned int tmp; ++ ++ /* ++ * Shut off the timer. ++ * Lock it in if it's a module and we defined ...NOWAYOUT ++ */ ++#ifndef CONFIG_WATCHDOG_NOWAYOUT ++ tmp = __raw_readl(wtcon); ++ tmp &= ~WT_ENABLE; ++ __raw_writel(tmp,wtcon); ++#endif ++ omahawdt_users = 0; ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++static ssize_t omahadog_write(struct file *file, const char *data, size_t len, loff_t *ppos) ++{ ++ volatile unsigned int wtcnt = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTCNT); ++ ++ /* Can't seek (pwrite) on this device */ ++ if (ppos != &file->f_pos) ++ return -ESPIPE; ++ ++ /* Refresh timer. */ ++ if(len) { ++ __raw_writel(pre_margin,wtcnt); ++ return 1; ++ } ++ return 0; ++} ++ ++static int omahadog_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ volatile unsigned int wtdat = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTDAT); ++ static struct watchdog_info ident = { ++ identity: "omaha Watchdog", ++ }; ++ ++ switch(cmd){ ++ default: ++ return -ENOIOCTLCMD; ++ case WDIOC_GETSUPPORT: ++ return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)); ++ case WDIOC_GETSTATUS: ++ return put_user(0,(int *)arg); ++ case WDIOC_GETBOOTSTATUS: ++ return 0; ++ case WDIOC_KEEPALIVE: ++ __raw_writel(pre_margin,wtdat); ++ return 0; ++ } ++} ++ ++static struct file_operations omahadog_fops= ++{ ++ .owner = THIS_MODULE, ++ .write = omahadog_write, ++ .ioctl = omahadog_ioctl, ++ .open = omahadog_open, ++ .release = omahadog_release, ++}; ++ ++static struct miscdevice omahadog_miscdev= ++{ ++ .minor = WATCHDOG_MINOR, ++ .name = "omaha watchdog", ++ .fops = &omahadog_fops ++}; ++ ++static int __init omahadog_init(void) ++{ ++ int ret; ++ ++ ret = misc_register(&omahadog_miscdev); ++ ++ if (ret) ++ return ret; ++ ++ printk("Omaha Watchdog Timer: timer margin %d sec\n", omaha_margin); ++ ++ return 0; ++} ++ ++static void __exit omahadog_exit(void) ++{ ++ misc_deregister(&omahadog_miscdev); ++} ++ ++module_init(omahadog_init); ++module_exit(omahadog_exit); +diff -urN linux-2.4.26/drivers/char/pcmcia/Config.in linux-2.4.26-vrs1/drivers/char/pcmcia/Config.in +--- linux-2.4.26/drivers/char/pcmcia/Config.in 2002-11-28 23:53:12.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/char/pcmcia/Config.in 2004-01-14 21:32:25.000000000 +0000 +@@ -5,7 +5,13 @@ + mainmenu_option next_comment + comment 'PCMCIA character devices' + +-dep_tristate 'PCMCIA serial device support' CONFIG_PCMCIA_SERIAL_CS $CONFIG_SERIAL ++if [ "$CONFIG_SERIAL" = "y" -o "$CONFIG_SERIAL_8250" = "y" ]; then ++ dep_tristate 'PCMCIA serial device support' CONFIG_PCMCIA_SERIAL_CS y ++else ++ if [ "$CONFIG_SERIAL" = "m" -o "$CONFIG_SERIAL_8250" = "m" ]; then ++ dep_tristate 'PCMCIA serial device support' CONFIG_PCMCIA_SERIAL_CS m ++ fi ++fi + if [ "$CONFIG_PCMCIA_SERIAL_CS" = "y" ]; then + define_bool CONFIG_PCMCIA_CHRDEV y + fi +diff -urN linux-2.4.26/drivers/char/pcmcia/memory_cs.c linux-2.4.26-vrs1/drivers/char/pcmcia/memory_cs.c +--- linux-2.4.26/drivers/char/pcmcia/memory_cs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/pcmcia/memory_cs.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,214 @@ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++static dev_info_t dev_info = "memory_cs"; ++static dev_link_t *dev_list; ++ ++struct memcs_dev { ++ dev_link_t link; ++ struct map_info map; ++}; ++ ++static __u8 mem_cs_read8(struct map_info *map, unsigned long ofs) ++{ ++ return readb(map->map_priv_1 + ofs); ++} ++ ++static __u16 mem_cs_read16(struct map_info *map, unsigned long ofs) ++{ ++ return readw(map->map_priv_1 + ofs); ++} ++ ++static __u32 mem_cs_read32(struct map_info *map, unsigned long ofs) ++{ ++ return readl(map->map_priv_1 + ofs); ++} ++ ++static void mem_cs_copy_from(struct map_info *map, void *to, unsigned long ofs, ssize_t size) ++{ ++ memcpy_fromio(to, map->map_priv_1 + ofs, size); ++} ++ ++static void mem_cs_write8(struct map_info *map, __u8 val, unsigned long ofs) ++{ ++ writeb(val, map->map_priv_1 + ofs); ++} ++ ++static void mem_cs_write16(struct map_info *map, __u16 val, unsigned long ofs) ++{ ++ writew(val, map->map_priv_1 + ofs); ++} ++ ++static void mem_cs_write32(struct map_info *map, __u32 val, unsigned long ofs) ++{ ++ writel(val, map->map_priv_1 + ofs); ++} ++ ++static void mem_cs_copy_to(struct map_info *map, unsigned long ofs, const void *to, ssize_t size) ++{ ++ memcpy_toio(map->map_priv_1 + ofs, from, size); ++} ++ ++static void mem_cs_release(u_long arg); ++ ++static void mem_cs_detach(dev_link_t *link) ++{ ++ del_timer(&link->release); ++ if (link->state & DEV_CONFIG) { ++ mem_cs_release((u_long)link); ++ if (link->state & DEV_STALE_CONFIG) { ++ link->state |= DEV_STALE_LINK; ++ return; ++ } ++ } ++ ++ if (link->handle) ++ CardServices(DeregisterClient, link->handle); ++ ++ kfree(link); ++} ++ ++static void mem_cs_release(u_long arg) ++{ ++ dev_link_t *link = (dev_link_t *)arg; ++ ++ link->dev = NULL; ++ if (link->win) { ++ CardServices(ReleaseWindow, link->win); ++ } ++ link->state &= ~DEV_CONFIG; ++ ++ if (link->state & DEV_STALE_LINK) ++ mem_cs_detach(link); ++} ++ ++static void mem_cs_config(dev_link_t *link) ++{ ++ struct memcs_dev *dev = link->priv; ++ cs_status_t status; ++ win_req_t req; ++ ++ link->state |= DEV_CONFIG; ++ ++ req.Attributes = word_width ? WIN_DATA_WIDTH_16 : WIN_DATA_WIDTH_8; ++ req.Base = 0; ++ req.Size = 0; ++ req.AccessSpeed = mem_speed; ++ ++ link->win = (window_handle_t)link->handle; ++ ++ CS_CHECK(RequestWindow, &link->win, &req); ++ ++ CS_CHECK(GetStatus, link->handle, &status); ++ ++ dev->map.buswidth = word_width ? 2 : 1; ++ dev->map.size = req.Size; ++ dev->map.map_priv_1 = ioremap(req.Base, req.Size); ++} ++ ++static int ++mem_cs_event(event_t event, int priority, event_callback_args_t *args) ++{ ++ dev_link_t *link = args->client_data; ++ ++ switch (event) { ++ case CS_EVENT_CARD_REMOVAL: ++ link->state &= ~DEV_PRESENT; ++ if (link->state & DEV_CONFIG) ++ mod_timer(&link->release, jiffies + HZ/20); ++ break; ++ ++ case CS_EVENT_CARD_INSERTION: ++ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; ++ mem_cs_config(link); ++ break; ++ } ++ return 0; ++} ++ ++static dev_link_t *mem_cs_attach(void) ++{ ++ struct memcs_dev *dev; ++ client_reg_t clnt; ++ ++ dev = kmalloc(sizeof(*dev), GFP_KERNEL); ++ if (dev) { ++ memset(dev, 0, sizeof(*dev)); ++ ++ dev->map.read8 = mem_cs_read8; ++ dev->map.read16 = mem_cs_read16; ++ dev->map.read32 = mem_cs_read32; ++ dev->map.copy_from = mem_cs_copy_from; ++ dev->map.write8 = mem_cs_write8; ++ dev->map.write16 = mem_cs_write16; ++ dev->map.write32 = mem_cs_write32; ++ dev->map.copy_to = mem_cs_copy_to; ++ ++ dev->link.release.function = &mem_cs_release; ++ dev->link.release.data = (u_long)link; ++ dev->link.priv = dev; ++ ++ dev->link.next = dev_list; ++ dev_list = &dev->link; ++ ++ clnt.dev_info = &dev_info; ++ clnt.Attributes = INOF_IO_CLIENT | INFO_CARD_SHARE; ++ clnt.EventMask = ++ CS_EVENT_WRITE_PROTECT | CS_EVENT_CARD_INSERTION | ++ CS_EVENT_CARD_REMOVAL | CS_EVENT_BATTERY_DEAD | ++ CS_EVENT_BATTERY_LOW; ++ ++ clnt.event_handler = &mem_cs_event; ++ clnt.Version = 0x0210; ++ clnt.event_callback_args.client_data = &dev->link; ++ ++ ret = CardServices(RegisterClient, &dev->link.handle, &clnt); ++ if (ret != CS_SUCCESS) { ++ error_info_t err = { RegisterClient, ret }; ++ CardServices(ReportError, dev->link.handle, &err); ++ mem_cs_detach(&dev->link); ++ dev = NULL; ++ } ++ } ++ ++ return &dev->link; ++} ++ ++static int __init mem_cs_init(void) ++{ ++ servinfo_t serv; ++ ++ CardServices(GetCardServicesInfo, &serv); ++ if (serv.Revision != CS_RELEASE_CODE) { ++ printk(KERN_NOTICE "memory_cs: Card services release " ++ "does not match\n"); ++ return -ENXIO; ++ } ++ register_pccard_driver(&dev_info, mem_cs_attach, mem_cs_detach); ++ return 0; ++} ++ ++static void __exit mem_cs_exit(void) ++{ ++ unregister_pccard_driver(&dev_info); ++ while (dev_list != NULL) ++ memory_detach(dev_list); ++} ++ ++module_init(mem_cs_init); ++module_exit(mem_cs_exit); ++ ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/char/sa1100-rtc.c linux-2.4.26-vrs1/drivers/char/sa1100-rtc.c +--- linux-2.4.26/drivers/char/sa1100-rtc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/sa1100-rtc.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,474 @@ ++/* ++ * Real Time Clock interface for Linux on StrongARM SA1100 ++ * ++ * Copyright (c) 2000 Nils Faerber ++ * ++ * Based on rtc.c by Paul Gortmaker ++ * Date/time conversion routines taken from arch/arm/kernel/time.c ++ * by Linus Torvalds and Russel King ++ * and the GNU C Library ++ * ( ... I love the GPL ... just take what you need! ;) ++ * ++ * 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. ++ * ++ * 1.00 2001-06-08 Nicolas Pitre ++ * - added periodic timer capability using OSMR1 ++ * - flag compatibility with other RTC chips ++ * - permission checks for ioctls ++ * - major cleanup, partial rewrite ++ * ++ * 0.03 2001-03-07 CIH ++ * - Modify the bug setups RTC clock. ++ * ++ * 0.02 2001-02-27 Nils Faerber ++ * - removed mktime(), added alarm irq clear ++ * ++ * 0.01 2000-10-01 Nils Faerber ++ * - initial release ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_VERSION "1.00" ++ ++#define TIMER_FREQ 3686400 ++ ++#define RTC_DEF_DIVIDER 32768 - 1 ++#define RTC_DEF_TRIM 0 ++ ++/* Those are the bits from a classic RTC we want to mimic */ ++#define RTC_IRQF 0x80 /* any of the following 3 is active */ ++#define RTC_PF 0x40 ++#define RTC_AF 0x20 ++#define RTC_UF 0x10 ++ ++static unsigned long rtc_status; ++static unsigned long rtc_irq_data; ++static unsigned long rtc_freq = 1024; ++ ++static struct fasync_struct *rtc_async_queue; ++static DECLARE_WAIT_QUEUE_HEAD(rtc_wait); ++ ++extern spinlock_t rtc_lock; ++ ++static const unsigned char days_in_mo[] = ++ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; ++ ++#define is_leap(year) \ ++ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) ++ ++/* ++ * Converts seconds since 1970-01-01 00:00:00 to Gregorian date. ++ */ ++ ++static void decodetime (unsigned long t, struct rtc_time *tval) ++{ ++ long days, month, year, rem; ++ ++ days = t / 86400; ++ rem = t % 86400; ++ tval->tm_hour = rem / 3600; ++ rem %= 3600; ++ tval->tm_min = rem / 60; ++ tval->tm_sec = rem % 60; ++ tval->tm_wday = (4 + days) % 7; ++ ++#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) ++ ++ year = 1970 + days / 365; ++ days -= ((year - 1970) * 365 ++ + LEAPS_THRU_END_OF (year - 1) ++ - LEAPS_THRU_END_OF (1970 - 1)); ++ if (days < 0) { ++ year -= 1; ++ days += 365 + is_leap(year); ++ } ++ tval->tm_year = year - 1900; ++ tval->tm_yday = days + 1; ++ ++ month = 0; ++ if (days >= 31) { ++ days -= 31; ++ month++; ++ if (days >= (28 + is_leap(year))) { ++ days -= (28 + is_leap(year)); ++ month++; ++ while (days >= days_in_mo[month]) { ++ days -= days_in_mo[month]; ++ month++; ++ } ++ } ++ } ++ tval->tm_mon = month; ++ tval->tm_mday = days + 1; ++} ++ ++static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ unsigned int rtsr = RTSR; ++ ++ /* clear interrupt sources */ ++ RTSR = 0; ++ RTSR = (RTSR_AL|RTSR_HZ); ++ ++ /* clear alarm interrupt if it has occurred */ ++ if (rtsr & RTSR_AL) ++ rtsr &= ~RTSR_ALE; ++ RTSR = rtsr & (RTSR_ALE|RTSR_HZE); ++ ++ /* update irq data & counter */ ++ if (rtsr & RTSR_AL) ++ rtc_irq_data |= (RTC_AF|RTC_IRQF); ++ if (rtsr & RTSR_HZ) ++ rtc_irq_data |= (RTC_UF|RTC_IRQF); ++ rtc_irq_data += 0x100; ++ ++ /* wake up waiting process */ ++ wake_up_interruptible(&rtc_wait); ++ kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); ++} ++ ++static void timer1_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ /* ++ * If we match for the first time, the periodic interrupt flag won't ++ * be set. If it is, then we did wrap around (very unlikely but ++ * still possible) and compute the amount of missed periods. ++ * The match reg is updated only when the data is actually retrieved ++ * to avoid unnecessary interrupts. ++ */ ++ OSSR = OSSR_M1; /* clear match on timer1 */ ++ if (rtc_irq_data & RTC_PF) { ++ rtc_irq_data += (rtc_freq * ((1<<30)/(TIMER_FREQ>>2))) << 8; ++ } else { ++ rtc_irq_data += (0x100|RTC_PF|RTC_IRQF); ++ } ++ ++ wake_up_interruptible(&rtc_wait); ++ kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); ++} ++ ++static int rtc_open(struct inode *inode, struct file *file) ++{ ++ if (test_and_set_bit (1, &rtc_status)) ++ return -EBUSY; ++ rtc_irq_data = 0; ++ return 0; ++} ++ ++static int rtc_release(struct inode *inode, struct file *file) ++{ ++ spin_lock_irq (&rtc_lock); ++ RTSR = 0; ++ RTSR = (RTSR_AL|RTSR_HZ); ++ OIER &= ~OIER_E1; ++ OSSR = OSSR_M1; ++ spin_unlock_irq (&rtc_lock); ++ rtc_status = 0; ++ return 0; ++} ++ ++static int rtc_fasync (int fd, struct file *filp, int on) ++{ ++ return fasync_helper (fd, filp, on, &rtc_async_queue); ++} ++ ++static unsigned int rtc_poll(struct file *file, poll_table *wait) ++{ ++ poll_wait (file, &rtc_wait, wait); ++ return (rtc_irq_data) ? 0 : POLLIN | POLLRDNORM; ++} ++ ++static loff_t rtc_llseek(struct file *file, loff_t offset, int origin) ++{ ++ return -ESPIPE; ++} ++ ++ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long data; ++ ssize_t retval; ++ ++ if (count < sizeof(unsigned long)) ++ return -EINVAL; ++ ++ add_wait_queue(&rtc_wait, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ for (;;) { ++ spin_lock_irq (&rtc_lock); ++ data = rtc_irq_data; ++ if (data != 0) { ++ rtc_irq_data = 0; ++ break; ++ } ++ spin_unlock_irq (&rtc_lock); ++ ++ if (file->f_flags & O_NONBLOCK) { ++ retval = -EAGAIN; ++ goto out; ++ } ++ ++ if (signal_pending(current)) { ++ retval = -ERESTARTSYS; ++ goto out; ++ } ++ ++ schedule(); ++ } ++ ++ if (data & RTC_PF) { ++ /* interpolate missed periods and set match for the next one */ ++ unsigned long period = TIMER_FREQ/rtc_freq; ++ unsigned long oscr = OSCR; ++ unsigned long osmr1 = OSMR1; ++ unsigned long missed = (oscr - osmr1)/period; ++ data += missed << 8; ++ OSSR = OSSR_M1; /* clear match on timer 1 */ ++ OSMR1 = osmr1 + (missed + 1)*period; ++ /* ensure we didn't miss another match in the mean time */ ++ while( (signed long)((osmr1 = OSMR1) - OSCR) <= 0 ) { ++ data += 0x100; ++ OSSR = OSSR_M1; /* clear match on timer 1 */ ++ OSMR1 = osmr1 + period; ++ } ++ } ++ spin_unlock_irq (&rtc_lock); ++ ++ data -= 0x100; /* the first IRQ wasn't actually missed */ ++ ++ retval = put_user(data, (unsigned long *)buf); ++ if (!retval) ++ retval = sizeof(unsigned long); ++ ++out: ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&rtc_wait, &wait); ++ return retval; ++} ++ ++static int rtc_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct rtc_time tm, tm2; ++ ++ switch (cmd) { ++ case RTC_AIE_OFF: ++ spin_lock_irq(&rtc_lock); ++ RTSR &= ~RTSR_ALE; ++ rtc_irq_data = 0; ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ case RTC_AIE_ON: ++ spin_lock_irq(&rtc_lock); ++ RTSR |= RTSR_ALE; ++ rtc_irq_data = 0; ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ case RTC_UIE_OFF: ++ spin_lock_irq(&rtc_lock); ++ RTSR &= ~RTSR_HZE; ++ rtc_irq_data = 0; ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ case RTC_UIE_ON: ++ spin_lock_irq(&rtc_lock); ++ RTSR |= RTSR_HZE; ++ rtc_irq_data = 0; ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ case RTC_PIE_OFF: ++ spin_lock_irq(&rtc_lock); ++ OIER &= ~OIER_E1; ++ rtc_irq_data = 0; ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ case RTC_PIE_ON: ++ if ((rtc_freq > 64) && !capable(CAP_SYS_RESOURCE)) ++ return -EACCES; ++ spin_lock_irq(&rtc_lock); ++ OSMR1 = TIMER_FREQ/rtc_freq + OSCR; ++ OIER |= OIER_E1; ++ rtc_irq_data = 0; ++ spin_unlock_irq(&rtc_lock); ++ return 0; ++ case RTC_ALM_READ: ++ decodetime (RTAR, &tm); ++ break; ++ case RTC_ALM_SET: ++ if (copy_from_user (&tm2, (struct rtc_time*)arg, sizeof (tm2))) ++ return -EFAULT; ++ decodetime (RCNR, &tm); ++ if ((unsigned)tm2.tm_hour < 24) ++ tm.tm_hour = tm2.tm_hour; ++ if ((unsigned)tm2.tm_min < 60) ++ tm.tm_min = tm2.tm_min; ++ if ((unsigned)tm2.tm_sec < 60) ++ tm.tm_sec = tm2.tm_sec; ++ RTAR = mktime ( tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ++ tm.tm_hour, tm.tm_min, tm.tm_sec); ++ return 0; ++ case RTC_RD_TIME: ++ decodetime (RCNR, &tm); ++ break; ++ case RTC_SET_TIME: ++ if (!capable(CAP_SYS_TIME)) ++ return -EACCES; ++ if (copy_from_user (&tm, (struct rtc_time*)arg, sizeof (tm))) ++ return -EFAULT; ++ tm.tm_year += 1900; ++ if (tm.tm_year < 1970 || (unsigned)tm.tm_mon >= 12 || ++ tm.tm_mday < 1 || tm.tm_mday > (days_in_mo[tm.tm_mon] + ++ (tm.tm_mon == 1 && is_leap(tm.tm_year))) || ++ (unsigned)tm.tm_hour >= 24 || ++ (unsigned)tm.tm_min >= 60 || ++ (unsigned)tm.tm_sec >= 60) ++ return -EINVAL; ++ RCNR = mktime ( tm.tm_year, tm.tm_mon + 1, tm.tm_mday, ++ tm.tm_hour, tm.tm_min, tm.tm_sec); ++ return 0; ++ case RTC_IRQP_READ: ++ return put_user(rtc_freq, (unsigned long *)arg); ++ case RTC_IRQP_SET: ++ if (arg < 1 || arg > TIMER_FREQ) ++ return -EINVAL; ++ if ((arg > 64) && (!capable(CAP_SYS_RESOURCE))) ++ return -EACCES; ++ rtc_freq = arg; ++ return 0; ++ case RTC_EPOCH_READ: ++ return put_user (1970, (unsigned long *)arg); ++ default: ++ return -EINVAL; ++ } ++ return copy_to_user ((void *)arg, &tm, sizeof (tm)) ? -EFAULT : 0; ++} ++ ++static struct file_operations rtc_fops = { ++ owner: THIS_MODULE, ++ llseek: rtc_llseek, ++ read: rtc_read, ++ poll: rtc_poll, ++ ioctl: rtc_ioctl, ++ open: rtc_open, ++ release: rtc_release, ++ fasync: rtc_fasync, ++}; ++ ++static struct miscdevice sa1100rtc_miscdev = { ++ RTC_MINOR, ++ "rtc", ++ &rtc_fops ++}; ++ ++static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) ++{ ++ char *p = page; ++ int len; ++ struct rtc_time tm; ++ ++ decodetime (RCNR, &tm); ++ p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" ++ "rtc_date\t: %04d-%02d-%02d\n" ++ "rtc_epoch\t: %04d\n", ++ tm.tm_hour, tm.tm_min, tm.tm_sec, ++ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1970); ++ decodetime (RTAR, &tm); ++ p += sprintf(p, "alrm_time\t: %02d:%02d:%02d\n" ++ "alrm_date\t: %04d-%02d-%02d\n", ++ tm.tm_hour, tm.tm_min, tm.tm_sec, ++ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); ++ p += sprintf(p, "trim/divider\t: 0x%08x\n", RTTR); ++ p += sprintf(p, "alarm_IRQ\t: %s\n", (RTSR & RTSR_ALE) ? "yes" : "no" ); ++ p += sprintf(p, "update_IRQ\t: %s\n", (RTSR & RTSR_HZE) ? "yes" : "no"); ++ p += sprintf(p, "periodic_IRQ\t: %s\n", (OIER & OIER_E1) ? "yes" : "no"); ++ p += sprintf(p, "periodic_freq\t: %ld\n", rtc_freq); ++ ++ len = (p - page) - off; ++ if (len < 0) ++ len = 0; ++ ++ *eof = (len <= count) ? 1 : 0; ++ *start = page + off; ++ ++ return len; ++} ++ ++static int __init rtc_init(void) ++{ ++ int ret; ++ ++ misc_register (&sa1100rtc_miscdev); ++ create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); ++ ret = request_irq (IRQ_RTC1Hz, rtc_interrupt, SA_INTERRUPT, "rtc 1Hz", NULL); ++ if (ret) { ++ printk (KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_RTC1Hz); ++ goto IRQ_RTC1Hz_failed; ++ } ++ ret = request_irq (IRQ_RTCAlrm, rtc_interrupt, SA_INTERRUPT, "rtc Alrm", NULL); ++ if (ret) { ++ printk(KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_RTCAlrm); ++ goto IRQ_RTCAlrm_failed; ++ } ++ ret = request_irq (IRQ_OST1, timer1_interrupt, SA_INTERRUPT, "rtc timer", NULL); ++ if (ret) { ++ printk(KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_OST1); ++ goto IRQ_OST1_failed; ++ } ++ ++ printk (KERN_INFO "SA1100 Real Time Clock driver v" DRIVER_VERSION "\n"); ++ ++ /* ++ * According to the manual we should be able to let RTTR be zero ++ * and then a default diviser for a 32.768KHz clock is used. ++ * Apparently this doesn't work, at least for my SA1110 rev 5. ++ * If the clock divider is uninitialized then reset it to the ++ * default value to get the 1Hz clock. ++ */ ++ if (RTTR == 0) { ++ RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); ++ printk (KERN_WARNING "rtc: warning: initializing default clock divider/trim value\n"); ++ /* The current RTC value probably doesn't make sense either */ ++ RCNR = 0; ++ } ++ ++ return 0; ++ ++IRQ_OST1_failed: ++ free_irq (IRQ_RTCAlrm, NULL); ++IRQ_RTCAlrm_failed: ++ free_irq (IRQ_RTC1Hz, NULL); ++IRQ_RTC1Hz_failed: ++ remove_proc_entry ("driver/rtc", NULL); ++ misc_deregister (&sa1100rtc_miscdev); ++ return ret; ++} ++ ++static void __exit rtc_exit(void) ++{ ++ free_irq (IRQ_OST1, NULL); ++ free_irq (IRQ_RTCAlrm, NULL); ++ free_irq (IRQ_RTC1Hz, NULL); ++ remove_proc_entry ("driver/rtc", NULL); ++ misc_deregister (&sa1100rtc_miscdev); ++} ++ ++module_init(rtc_init); ++module_exit(rtc_exit); ++ ++MODULE_AUTHOR("Nils Faerber "); ++MODULE_DESCRIPTION("SA1100 Realtime Clock Driver (RTC)"); ++EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/char/sa1100_wdt.c linux-2.4.26-vrs1/drivers/char/sa1100_wdt.c +--- linux-2.4.26/drivers/char/sa1100_wdt.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/sa1100_wdt.c 2004-04-10 12:04:15.000000000 +0100 +@@ -0,0 +1,150 @@ ++/* ++ * Watchdog driver for the SA11x0 ++ * ++ * (c) Copyright 2000 Oleg Drokin ++ * Based on SoftDog driver by Alan Cox ++ * ++ * 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. ++ * ++ * Neither Oleg Drokin nor iXcelerator.com admit liability nor provide ++ * warranty for any of this software. This material is provided ++ * "AS-IS" and at no charge. ++ * ++ * (c) Copyright 2000 Oleg Drokin ++ * ++ * 27/11/2000 Initial release ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TIMER_MARGIN 60 /* (secs) Default is 1 minute */ ++ ++static int sa1100_margin = TIMER_MARGIN; /* in seconds */ ++static int sa1100wdt_users; ++static int pre_margin; ++#ifdef MODULE ++MODULE_PARM(sa1100_margin,"i"); ++#endif ++ ++/* ++ * Allow only one person to hold it open ++ */ ++ ++static int sa1100dog_open(struct inode *inode, struct file *file) ++{ ++ if(test_and_set_bit(1,&sa1100wdt_users)) ++ return -EBUSY; ++ MOD_INC_USE_COUNT; ++ /* Activate SA1100 Watchdog timer */ ++ pre_margin=3686400 * sa1100_margin; ++ OSMR3 = OSCR + pre_margin; ++ OSSR = OSSR_M3; ++ OWER = OWER_WME; ++ OIER |= OIER_E3; ++ return 0; ++} ++ ++static int sa1100dog_release(struct inode *inode, struct file *file) ++{ ++ /* ++ * Shut off the timer. ++ * Lock it in if it's a module and we defined ...NOWAYOUT ++ */ ++ OSMR3 = OSCR + pre_margin; ++#ifndef CONFIG_WATCHDOG_NOWAYOUT ++ OIER &= ~OIER_E3; ++#endif ++ sa1100wdt_users = 0; ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++static ssize_t sa1100dog_write(struct file *file, const char *data, size_t len, loff_t *ppos) ++{ ++ /* Can't seek (pwrite) on this device */ ++ if (ppos != &file->f_pos) ++ return -ESPIPE; ++ ++ /* Refresh OSMR3 timer. */ ++ if(len) { ++ OSMR3 = OSCR + pre_margin; ++ return 1; ++ } ++ return 0; ++} ++ ++static int sa1100dog_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ static struct watchdog_info ident = { ++ identity: "SA1100 Watchdog", ++ }; ++ ++ switch(cmd){ ++ default: ++ return -ENOIOCTLCMD; ++ case WDIOC_GETSUPPORT: ++ return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)); ++ case WDIOC_GETSTATUS: ++ return put_user(0,(int *)arg); ++ case WDIOC_GETBOOTSTATUS: ++ return put_user((RCSR & RCSR_WDR) ? WDIOF_CARDRESET : 0, (int *)arg); ++ case WDIOC_KEEPALIVE: ++ OSMR3 = OSCR + pre_margin; ++ return 0; ++ } ++} ++ ++static struct file_operations sa1100dog_fops= ++{ ++ owner: THIS_MODULE, ++ write: sa1100dog_write, ++ ioctl: sa1100dog_ioctl, ++ open: sa1100dog_open, ++ release: sa1100dog_release, ++}; ++ ++static struct miscdevice sa1100dog_miscdev= ++{ ++ WATCHDOG_MINOR, ++ "SA1100 watchdog", ++ &sa1100dog_fops ++}; ++ ++static int __init sa1100dog_init(void) ++{ ++ int ret; ++ ++ ret = misc_register(&sa1100dog_miscdev); ++ ++ if (ret) ++ return ret; ++ ++ printk("SA1100 Watchdog Timer: timer margin %d sec\n", sa1100_margin); ++ ++ return 0; ++} ++ ++static void __exit sa1100dog_exit(void) ++{ ++ misc_deregister(&sa1100dog_miscdev); ++} ++ ++module_init(sa1100dog_init); ++module_exit(sa1100dog_exit); +diff -urN linux-2.4.26/drivers/char/sa1111_keyb.c linux-2.4.26-vrs1/drivers/char/sa1111_keyb.c +--- linux-2.4.26/drivers/char/sa1111_keyb.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/sa1111_keyb.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,1153 @@ ++/* ++ * SA1111 PS/2 keyboard/mouse driver ++ * ++ * 2000 by VASARA RESEARCH INC. ++ * ++ * Changelog: ++ * Jun.xx,2000: Kunihiko IMAI ++ * Port to 2.4.0test1-ac19-rmk1-np1 ++ * Apr.17,2000: Takafumi Kawana ++ * Internal Release for XP860 ++ * ++ * ++ * This driver is based on linux/drivers/char/pc_keyb.c ++ * Original declaration follows: ++ ++ * ++ * linux/drivers/char/pc_keyb.c ++ * ++ * Separation of the PC low-level part by Geert Uytterhoeven, May 1997 ++ * See keyboard.c for the whole history. ++ * ++ * Major cleanup by Martin Mares, May 1997 ++ * ++ * Combined the keyboard and PS/2 mouse handling into one file, ++ * because they share the same hardware. ++ * Johan Myreen 1998-10-08. ++ * ++ * Code fixes to handle mouse ACKs properly. ++ * C. Scott Ananian 1999-01-29. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* Some configuration switches are present in the include file... */ ++ ++#include ++#include ++#include ++ ++#define KBD_STAT_RXB (1<<4) ++#define KBD_STAT_RXF (1<<5) ++#define KBD_STAT_TXB (1<<6) ++#define KBD_STAT_TXE (1<<7) ++#define KBD_STAT_STP (1<<8) ++ ++#define MSE_STAT_RXB (1<<4) ++#define MSE_STAT_RXF (1<<5) ++#define MSE_STAT_TXB (1<<6) ++#define MSE_STAT_TXE (1<<7) ++#define MSE_STAT_STP (1<<8) ++ ++/* Simple translation table for the SysRq keys */ ++ ++#ifdef CONFIG_MAGIC_SYSRQ ++unsigned char sa1111_sysrq_xlate[128] = "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ ++ "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ ++ "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ ++ "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ ++ "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ ++ "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ ++ "\r\000/"; /* 0x60 - 0x6f */ ++#endif ++ ++// static void kbd_write_command_w(int data); ++static void kbd_write_output_w(int data); ++ ++spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; ++static void handle_kbd_event(void); ++ ++/* used only by send_data - set by keyboard_interrupt */ ++static volatile unsigned char reply_expected = 0; ++static volatile unsigned char acknowledge = 0; ++static volatile unsigned char resend = 0; ++ ++ ++#if defined CONFIG_PSMOUSE ++/* ++ * PS/2 Auxiliary Device ++ */ ++ ++static int __init psaux_init(void); ++ ++static struct aux_queue *queue; /* Mouse data buffer. */ ++static int aux_count = 0; ++/* used when we send commands to the mouse that expect an ACK. */ ++static unsigned char mouse_reply_expected = 0; ++ ++#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT) ++#define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT) ++ ++#define MAX_RETRIES 60 /* some aux operations take long time */ ++#endif /* CONFIG_PSMOUSE */ ++ ++/* ++ * Wait for keyboard controller input buffer to drain. ++ * ++ * Don't use 'jiffies' so that we don't depend on ++ * interrupts.. ++ * ++ * Quote from PS/2 System Reference Manual: ++ * ++ * "Address hex 0060 and address hex 0064 should be written only when ++ * the input-buffer-full bit and output-buffer-full bit in the ++ * Controller Status register are set 0." ++ */ ++ ++static void kb_wait(void) ++{ ++ unsigned long timeout = KBC_TIMEOUT; ++ ++ do { ++ /* ++ * "handle_kbd_event()" will handle any incoming events ++ * while we wait - keypresses or mouse movement. ++ */ ++ handle_kbd_event(); ++ if (KBDSTAT & KBD_STAT_TXE) ++ return; ++ mdelay(1); ++ timeout--; ++ } ++ while (timeout); ++#ifdef KBD_REPORT_TIMEOUTS ++ printk(KERN_WARNING "Keyboard timed out[1]\n"); ++#endif ++} ++ ++/* ++ * Translation of escaped scancodes to keycodes. ++ * This is now user-settable. ++ * The keycodes 1-88,96-111,119 are fairly standard, and ++ * should probably not be changed - changing might confuse X. ++ * X also interprets scancode 0x5d (KEY_Begin). ++ * ++ * For 1-88 keycode equals scancode. ++ */ ++ ++#define E0_KPENTER 96 ++#define E0_RCTRL 97 ++#define E0_KPSLASH 98 ++#define E0_PRSCR 99 ++#define E0_RALT 100 ++#define E0_BREAK 101 /* (control-pause) */ ++#define E0_HOME 102 ++#define E0_UP 103 ++#define E0_PGUP 104 ++#define E0_LEFT 105 ++#define E0_RIGHT 106 ++#define E0_END 107 ++#define E0_DOWN 108 ++#define E0_PGDN 109 ++#define E0_INS 110 ++#define E0_DEL 111 ++ ++/* for USB 106 keyboard */ ++#define E0_YEN 124 ++#define E0_BACKSLASH 89 ++ ++ ++#define E1_PAUSE 119 ++ ++/* ++ * The keycodes below are randomly located in 89-95,112-118,120-127. ++ * They could be thrown away (and all occurrences below replaced by 0), ++ * but that would force many users to use the `setkeycodes' utility, where ++ * they needed not before. It does not matter that there are duplicates, as ++ * long as no duplication occurs for any single keyboard. ++ */ ++#define SC_LIM 89 ++ ++#define FOCUS_PF1 85 /* actual code! */ ++#define FOCUS_PF2 89 ++#define FOCUS_PF3 90 ++#define FOCUS_PF4 91 ++#define FOCUS_PF5 92 ++#define FOCUS_PF6 93 ++#define FOCUS_PF7 94 ++#define FOCUS_PF8 95 ++#define FOCUS_PF9 120 ++#define FOCUS_PF10 121 ++#define FOCUS_PF11 122 ++#define FOCUS_PF12 123 ++ ++#define JAP_86 124 ++/* tfj@olivia.ping.dk: ++ * The four keys are located over the numeric keypad, and are ++ * labelled A1-A4. It's an rc930 keyboard, from ++ * Regnecentralen/RC International, Now ICL. ++ * Scancodes: 59, 5a, 5b, 5c. ++ */ ++#define RGN1 124 ++#define RGN2 125 ++#define RGN3 126 ++#define RGN4 127 ++ ++static unsigned char high_keys[128 - SC_LIM] = { ++ RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ ++ 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ ++ 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ ++ FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ ++ FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ ++}; ++ ++/* BTC */ ++#define E0_MACRO 112 ++/* LK450 */ ++#define E0_F13 113 ++#define E0_F14 114 ++#define E0_HELP 115 ++#define E0_DO 116 ++#define E0_F17 117 ++#define E0_KPMINPLUS 118 ++/* ++ * My OmniKey generates e0 4c for the "OMNI" key and the ++ * right alt key does nada. [kkoller@nyx10.cs.du.edu] ++ */ ++#define E0_OK 124 ++/* ++ * New microsoft keyboard is rumoured to have ++ * e0 5b (left window button), e0 5c (right window button), ++ * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] ++ * [or: Windows_L, Windows_R, TaskMan] ++ */ ++#define E0_MSLW 125 ++#define E0_MSRW 126 ++#define E0_MSTM 127 ++ ++static unsigned char e0_keys[128] = { ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ ++ 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ ++ 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ ++ E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ ++ E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ ++ E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END, /* 0x48-0x4f */ ++ E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ ++ 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ ++ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ ++ 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ ++ //0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ ++ 0, 0, 0, 0, 0, E0_BACKSLASH, 0, 0, /* 0x70-0x77 */ ++ 0, 0, 0, E0_YEN, 0, 0, 0, 0 /* 0x78-0x7f */ ++}; ++ ++int sa1111_setkeycode(unsigned int scancode, unsigned int keycode) ++{ ++ if (scancode < SC_LIM || scancode > 255 || keycode > 127) ++ return -EINVAL; ++ if (scancode < 128) ++ high_keys[scancode - SC_LIM] = keycode; ++ else ++ e0_keys[scancode - 128] = keycode; ++ return 0; ++} ++ ++int sa1111_getkeycode(unsigned int scancode) ++{ ++ return ++ (scancode < SC_LIM || scancode > 255) ? -EINVAL : ++ (scancode < ++ 128) ? high_keys[scancode - SC_LIM] : e0_keys[scancode - 128]; ++} ++ ++static int do_acknowledge(unsigned char scancode) ++{ ++ if (reply_expected) { ++ /* Unfortunately, we must recognise these codes only if we know they ++ * are known to be valid (i.e., after sending a command), because there ++ * are some brain-damaged keyboards (yes, FOCUS 9000 again) which have ++ * keys with such codes :( ++ */ ++ if (scancode == KBD_REPLY_ACK) { ++ acknowledge = 1; ++ reply_expected = 0; ++ return 0; ++ } else if (scancode == KBD_REPLY_RESEND) { ++ resend = 1; ++ reply_expected = 0; ++ return 0; ++ } ++ /* Should not happen... */ ++#if 0 ++ printk(KERN_DEBUG "keyboard reply expected - got %02x\n", ++ scancode); ++#endif ++ } ++ return 1; ++} ++ ++int ++sa1111_translate(unsigned char scancode, unsigned char *keycode, ++ char raw_mode) ++{ ++ static int prev_scancode = 0; ++ ++ /* special prefix scancodes.. */ ++ if (scancode == 0xe0 || scancode == 0xe1) { ++ prev_scancode = scancode; ++ return 0; ++ } ++ ++ /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */ ++ if (scancode == 0x00 || scancode == 0xff) { ++ prev_scancode = 0; ++ return 0; ++ } ++ ++ scancode &= 0x7f; ++ ++ if (prev_scancode) { ++ /* ++ * usually it will be 0xe0, but a Pause key generates ++ * e1 1d 45 e1 9d c5 when pressed, and nothing when released ++ */ ++ if (prev_scancode != 0xe0) { ++ if (prev_scancode == 0xe1 && scancode == 0x1d) { ++ prev_scancode = 0x100; ++ return 0; ++ } ++ else if (prev_scancode == 0x100 ++ && scancode == 0x45) { ++ *keycode = E1_PAUSE; ++ prev_scancode = 0; ++ } else { ++#ifdef KBD_REPORT_UNKN ++ if (!raw_mode) ++ printk(KERN_INFO ++ "keyboard: unknown e1 escape sequence\n"); ++#endif ++ prev_scancode = 0; ++ return 0; ++ } ++ } else { ++ prev_scancode = 0; ++ /* ++ * The keyboard maintains its own internal caps lock and ++ * num lock statuses. In caps lock mode E0 AA precedes make ++ * code and E0 2A follows break code. In num lock mode, ++ * E0 2A precedes make code and E0 AA follows break code. ++ * We do our own book-keeping, so we will just ignore these. ++ */ ++ /* ++ * For my keyboard there is no caps lock mode, but there are ++ * both Shift-L and Shift-R modes. The former mode generates ++ * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. ++ * So, we should also ignore the latter. - aeb@cwi.nl ++ */ ++ if (scancode == 0x2a || scancode == 0x36) ++ return 0; ++ ++ if (e0_keys[scancode]) ++ *keycode = e0_keys[scancode]; ++ else { ++#ifdef KBD_REPORT_UNKN ++ if (!raw_mode) ++ printk(KERN_INFO ++ "keyboard: unknown scancode e0 %02x\n", ++ scancode); ++#endif ++ return 0; ++ } ++ } ++ } else if (scancode >= SC_LIM) { ++ /* This happens with the FOCUS 9000 keyboard ++ Its keys PF1..PF12 are reported to generate ++ 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f ++ Moreover, unless repeated, they do not generate ++ key-down events, so we have to zero up_flag below */ ++ /* Also, Japanese 86/106 keyboards are reported to ++ generate 0x73 and 0x7d for \ - and \ | respectively. */ ++ /* Also, some Brazilian keyboard is reported to produce ++ 0x73 and 0x7e for \ ? and KP-dot, respectively. */ ++ ++ *keycode = high_keys[scancode - SC_LIM]; ++ ++ if (!*keycode) { ++ if (!raw_mode) { ++#ifdef KBD_REPORT_UNKN ++ printk(KERN_INFO ++ "keyboard: unrecognized scancode (%02x)" ++ " - ignored\n", scancode); ++#endif ++ } ++ return 0; ++ } ++ } else ++ *keycode = scancode; ++ return 1; ++} ++ ++char sa1111_unexpected_up(unsigned char keycode) ++{ ++ /* unexpected, but this can happen: maybe this was a key release for a ++ FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */ ++ if (keycode >= SC_LIM || keycode == 85) ++ return 0; ++ else ++ return 0200; ++} ++ ++static unsigned char kbd_exists = 1; ++ ++static inline void handle_keyboard_event(unsigned char scancode) ++{ ++#ifdef CONFIG_VT ++ kbd_exists = 1; ++ if (do_acknowledge(scancode)) ++ handle_scancode(scancode, !(scancode & 0x80)); ++#endif ++ tasklet_schedule(&keyboard_tasklet); ++} ++ ++/* ++ * This reads the keyboard status port, and does the ++ * appropriate action. ++ * ++ * It requires that we hold the keyboard controller ++ * spinlock. ++ */ ++static void handle_kbd_event(void) ++{ ++ unsigned int status = KBDSTAT; ++ unsigned int work = 10000; ++ unsigned char scancode; ++ ++ while (status & KBD_STAT_RXF) { ++ while (status & KBD_STAT_RXF) { ++ scancode = KBDDATA & 0xff; ++ if (!(status & KBD_STAT_STP)) ++ handle_keyboard_event(scancode); ++ if (!--work) { ++ printk(KERN_ERR ++ "pc_keyb: keyboard controller jammed (0x%02X).\n", ++ status); ++ return; ++ } ++ status = KBDSTAT; ++ } ++ work = 10000; ++ } ++ ++ if (status & KBD_STAT_STP) ++ KBDSTAT = KBD_STAT_STP; ++} ++ ++static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ unsigned long flags; ++ ++#ifdef CONFIG_VT ++ kbd_pt_regs = regs; ++#endif ++ spin_lock_irqsave(&kbd_controller_lock, flags); ++ handle_kbd_event(); ++ spin_unlock_irqrestore(&kbd_controller_lock, flags); ++} ++ ++/* ++ * send_data sends a character to the keyboard and waits ++ * for an acknowledge, possibly retrying if asked to. Returns ++ * the success status. ++ * ++ * Don't use 'jiffies', so that we don't depend on interrupts ++ */ ++static int send_data(unsigned char data) ++{ ++ int retries = 3; ++ ++ do { ++ unsigned long timeout = KBD_TIMEOUT; ++ ++ acknowledge = 0; /* Set by interrupt routine on receipt of ACK. */ ++ resend = 0; ++ reply_expected = 1; ++ kbd_write_output_w(data); ++ for (;;) { ++ if (acknowledge) ++ return 1; ++ if (resend) ++ break; ++ mdelay(1); ++ if (!--timeout) { ++#ifdef KBD_REPORT_TIMEOUTS ++ printk(KERN_WARNING ++ "keyboard: Timeout - AT keyboard not present?\n"); ++#endif ++ return 0; ++ } ++ } ++ } ++ while (retries-- > 0); ++#ifdef KBD_REPORT_TIMEOUTS ++ printk(KERN_WARNING ++ "keyboard: Too many NACKs -- noisy kbd cable?\n"); ++#endif ++ return 0; ++} ++ ++void sa1111_leds(unsigned char leds) ++{ ++ if (kbd_exists ++ && (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))) { ++ send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */ ++ kbd_exists = 0; ++ } ++} ++ ++#define KBD_NO_DATA (-1) /* No data */ ++#define KBD_BAD_DATA (-2) /* Parity or other error */ ++ ++static int __init kbd_read_data(void) ++{ ++ int retval = KBD_NO_DATA; ++ unsigned int status; ++ ++ status = KBDSTAT; ++ if (status & KBD_STAT_RXF) { ++ unsigned char data = KBDDATA; ++ ++ retval = data; ++ if (status & KBD_STAT_STP) ++ retval = KBD_BAD_DATA; ++ } ++ return retval; ++} ++ ++static void __init kbd_clear_input(void) ++{ ++ int maxread = 100; /* Random number */ ++ ++ do { ++ if (kbd_read_data() == KBD_NO_DATA) ++ break; ++ } ++ while (--maxread); ++} ++ ++static int __init kbd_wait_for_input(void) ++{ ++ long timeout = KBD_INIT_TIMEOUT; ++ ++ do { ++ int retval = kbd_read_data(); ++ if (retval >= 0) ++ return retval; ++ mdelay(1); ++ } ++ while (--timeout); ++ return -1; ++} ++ ++#if 0 ++static void kbd_write_command_w(int data) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&kbd_controller_lock, flags); ++ kb_wait(); ++ kbd_write_command(data); ++ spin_unlock_irqrestore(&kbd_controller_lock, flags); ++} ++#endif ++ ++static void kbd_write_output_w(int data) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&kbd_controller_lock, flags); ++ kb_wait(); ++ KBDDATA = data & 0xff; ++ spin_unlock_irqrestore(&kbd_controller_lock, flags); ++} ++ ++/* ++ * Test the keyboard interface. We basically check to make sure that ++ * we can drive each line to the keyboard independently of each other. ++ */ ++static int kbdif_test(void) ++{ ++ int ret = 0; ++ ++ KBDCR = KBDCR_ENA | KBDCR_FKC; ++ udelay(2); ++ if ((KBDSTAT & (KBDSTAT_KBC | KBDSTAT_KBD)) != KBDSTAT_KBD) { ++ printk("Keyboard interface test failed[1]: %02x\n", ++ KBDSTAT); ++ ret = -ENODEV; ++ } ++ ++ KBDCR = KBDCR_ENA; ++ udelay(2); ++ if ((KBDSTAT & (KBDSTAT_KBC | KBDSTAT_KBD)) != (KBDSTAT_KBC | KBDSTAT_KBD)) { ++ printk("Keyboard interface test failed[2]: %02x\n", ++ KBDSTAT); ++ ret = -ENODEV; ++ } ++ ++ KBDCR = KBDCR_ENA | KBDCR_FKD; ++ udelay(2); ++ if ((KBDSTAT & (KBDSTAT_KBC | KBDSTAT_KBD)) != KBDSTAT_KBC) { ++ printk("Keyboard interface test failed[3]: %02x\n", ++ KBDSTAT); ++ ret = -ENODEV; ++ } ++ ++ return ret; ++} ++ ++static char *__init initialize_kbd(void) ++{ ++ int status; ++ ++ /* ++ * Test the keyboard interface. ++ */ ++ kbdif_test(); ++ ++ /* ++ * Ok, drop the force low bits, and wait a while, ++ * and clear the stop bit error flag. ++ */ ++ KBDCR = KBDCR_ENA; ++ udelay(4); ++ KBDSTAT = KBD_STAT_STP; ++ ++ /* ++ * Ok, we're now ready to talk to the keyboard. Reset ++ * it, just to make sure we're starting in a sane state. ++ * ++ * Set up to try again if the keyboard asks for RESEND. ++ */ ++ do { ++ KBDDATA = KBD_CMD_RESET; ++ status = kbd_wait_for_input(); ++ if (status == KBD_REPLY_ACK) ++ break; ++ if (status != KBD_REPLY_RESEND) ++ return "Keyboard reset failed, no ACK"; ++ } while (1); ++ ++ if (kbd_wait_for_input() != KBD_REPLY_POR) ++ return "Keyboard reset failed, no POR"; ++ ++ /* ++ * Set keyboard controller mode. During this, the keyboard should be ++ * in the disabled state. ++ * ++ * Set up to try again if the keyboard asks for RESEND. ++ */ ++ do { ++ kbd_write_output_w(KBD_CMD_DISABLE); ++ status = kbd_wait_for_input(); ++ if (status == KBD_REPLY_ACK) ++ break; ++ if (status != KBD_REPLY_RESEND) ++ return "Disable keyboard: no ACK"; ++ } while (1); ++ ++#if 0 /*@@@ */ ++ kbd_write_command_w(KBD_CCMD_WRITE_MODE); ++ kbd_write_output_w(KBD_MODE_KBD_INT ++ | KBD_MODE_SYS | KBD_MODE_DISABLE_MOUSE | ++ KBD_MODE_KCC); ++ ++ /* ibm powerpc portables need this to use scan-code set 1 -- Cort */ ++ kbd_write_command_w(KBD_CCMD_READ_MODE); ++ if (!(kbd_wait_for_input() & KBD_MODE_KCC)) { ++ /* ++ * If the controller does not support conversion, ++ * Set the keyboard to scan-code set 1. ++ */ ++ kbd_write_output_w(0xF0); ++ kbd_wait_for_input(); ++ kbd_write_output_w(0x01); ++ kbd_wait_for_input(); ++ } ++#else ++ kbd_write_output_w(0xf0); ++ kbd_wait_for_input(); ++ kbd_write_output_w(0x01); ++ kbd_wait_for_input(); ++#endif ++ ++ ++ kbd_write_output_w(KBD_CMD_ENABLE); ++ if (kbd_wait_for_input() != KBD_REPLY_ACK) ++ return "Enable keyboard: no ACK"; ++ ++ /* ++ * Finally, set the typematic rate to maximum. ++ */ ++ kbd_write_output_w(KBD_CMD_SET_RATE); ++ if (kbd_wait_for_input() != KBD_REPLY_ACK) ++ return "Set rate: no ACK"; ++ kbd_write_output_w(0x00); ++ if (kbd_wait_for_input() != KBD_REPLY_ACK) ++ return "Set rate: no ACK"; ++ ++ return NULL; ++} ++ ++int __init sa1111_kbd_init_hw(void) ++{ ++ char *msg; ++ int ret; ++ ++ if (!request_mem_region(_KBDCR, 512, "keyboard")) ++ return -EBUSY; ++ ++ SKPCR |= SKPCR_PTCLKEN; ++ KBDCLKDIV = 0; ++ KBDPRECNT = 127; ++ ++ /* Flush any pending input. */ ++ kbd_clear_input(); ++ ++ msg = initialize_kbd(); ++ if (msg) ++ printk(KERN_WARNING "initialize_kbd: %s\n", msg); ++ ++#if defined CONFIG_PSMOUSE ++ psaux_init(); ++#endif ++ ++ k_setkeycode = sa1111_setkeycode; ++ k_getkeycode = sa1111_getkeycode; ++ k_translate = sa1111_translate; ++ k_unexpected_up = sa1111_unexpected_up; ++ k_leds = sa1111_leds; ++#ifdef CONFIG_MAGIC_SYSRQ ++ k_sysrq_xlate = sa1111_sysrq_xlate; ++ k_sysrq_key = 0x54; ++#endif ++ ++ /* Ok, finally allocate the IRQ, and off we go.. */ ++ ret = request_irq(IRQ_TPRXINT, keyboard_interrupt, 0, "keyboard", NULL); ++ if (ret) ++ release_mem_region(_KBDCR, 512); ++ ++ return ret; ++} ++ ++#if defined CONFIG_PSMOUSE ++ ++static inline void handle_mouse_event(unsigned char scancode) ++{ ++ if (mouse_reply_expected) { ++ if (scancode == AUX_ACK) { ++ mouse_reply_expected--; ++ return; ++ } ++ mouse_reply_expected = 0; ++ } ++ ++ add_mouse_randomness(scancode); ++ if (aux_count) { ++ int head = queue->head; ++ ++ queue->buf[head] = scancode; ++ head = (head + 1) & (AUX_BUF_SIZE - 1); ++ if (head != queue->tail) { ++ queue->head = head; ++ if (queue->fasync) ++ kill_fasync(&queue->fasync, SIGIO, ++ POLL_IN); ++ wake_up_interruptible(&queue->proc_list); ++ } ++ } ++} ++ ++static void handle_mse_event(void) ++{ ++ unsigned int msests = MSESTAT; ++ unsigned int work = 10000; ++ unsigned char scancode; ++ ++ while (msests & MSE_STAT_RXF) { ++ while (msests & MSE_STAT_RXF) { ++ scancode = MSEDATA & 0xff; ++ if (!(msests & MSE_STAT_STP)) ++ handle_mouse_event(scancode); ++ if (!--work) { ++ printk(KERN_ERR ++ "pc_keyb: mouse controller jammed (0x%02X).\n", ++ msests); ++ return; ++ /*XXX*/} ++ msests = MSESTAT; ++ } ++ work = 10000; ++ } ++} ++ ++static void ms_wait(void) ++{ ++ unsigned long timeout = KBC_TIMEOUT; ++ ++ do { ++ /* ++ * "handle_kbd_event()" will handle any incoming events ++ * while we wait - keypresses or mouse movement. ++ */ ++ handle_mse_event(); ++ if (MSESTAT & MSE_STAT_TXE) ++ return; ++ mdelay(1); ++ timeout--; ++ } ++ while (timeout); ++#ifdef KBD_REPORT_TIMEOUTS ++ printk(KERN_WARNING "Mouse timed out[1]\n"); ++#endif ++} ++ ++static void mouse_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&kbd_controller_lock, flags); ++ handle_mse_event(); ++ spin_unlock_irqrestore(&kbd_controller_lock, flags); ++} ++ ++/* ++ * Check if this is a dual port controller. ++ */ ++static int __init detect_auxiliary_port(void) ++{ ++ unsigned long flags; ++ int loops = 10; ++ int retval = 0; ++ ++ /* Check if the BIOS detected a device on the auxiliary port. */ ++ if (aux_device_present == 0xaa) ++ return 1; ++ ++ spin_lock_irqsave(&kbd_controller_lock, flags); ++ ++ /* Put the value 0x5A in the output buffer using the "Write ++ * Auxiliary Device Output Buffer" command (0xD3). Poll the ++ * Status Register for a while to see if the value really ++ * turns up in the Data Register. If the KBD_STAT_MOUSE_OBF ++ * bit is also set to 1 in the Status Register, we assume this ++ * controller has an Auxiliary Port (a.k.a. Mouse Port). ++ */ ++ // kb_wait(); ++ // kbd_write_command(KBD_CCMD_WRITE_AUX_OBUF); ++ ++ SKPCR |= SKPCR_PMCLKEN; ++ ++ MSECLKDIV = 0; ++ MSEPRECNT = 127; ++ MSECR = MSECR_ENA; ++ mdelay(50); ++ MSEDATA = 0xf4; ++ mdelay(50); ++ ++ do { ++ unsigned int msests = MSESTAT; ++ ++ if (msests & MSE_STAT_RXF) { ++ do { ++ msests = MSEDATA; /* dummy read */ ++ mdelay(50); ++ msests = MSESTAT; ++ } ++ while (msests & MSE_STAT_RXF); ++ printk(KERN_INFO "Detected PS/2 Mouse Port.\n"); ++ retval = 1; ++ break; ++ } ++ mdelay(1); ++ } ++ while (--loops); ++ spin_unlock_irqrestore(&kbd_controller_lock, flags); ++ ++ return retval; ++} ++ ++/* ++ * Send a byte to the mouse. ++ */ ++static void aux_write_dev(int val) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&kbd_controller_lock, flags); ++ // kb_wait(); ++ // kbd_write_command(KBD_CCMD_WRITE_MOUSE); ++ ms_wait(); ++ MSEDATA = val; ++ spin_unlock_irqrestore(&kbd_controller_lock, flags); ++} ++ ++/* ++ * Send a byte to the mouse & handle returned ack ++ */ ++static void aux_write_ack(int val) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&kbd_controller_lock, flags); ++ // kb_wait(); ++ // kbd_write_command(KBD_CCMD_WRITE_MOUSE); ++ ms_wait(); ++ MSEDATA = val; ++ /* we expect an ACK in response. */ ++ mouse_reply_expected++; ++ ms_wait(); ++ spin_unlock_irqrestore(&kbd_controller_lock, flags); ++} ++ ++static unsigned char get_from_queue(void) ++{ ++ unsigned char result; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&kbd_controller_lock, flags); ++ result = queue->buf[queue->tail]; ++ queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE - 1); ++ spin_unlock_irqrestore(&kbd_controller_lock, flags); ++ return result; ++} ++ ++ ++static inline int queue_empty(void) ++{ ++ return queue->head == queue->tail; ++} ++ ++static int fasync_aux(int fd, struct file *filp, int on) ++{ ++ int retval; ++ ++ retval = fasync_helper(fd, filp, on, &queue->fasync); ++ if (retval < 0) ++ return retval; ++ return 0; ++} ++ ++ ++/* ++ * Random magic cookie for the aux device ++ */ ++#define AUX_DEV ((void *)queue) ++ ++static int release_aux(struct inode *inode, struct file *file) ++{ ++ fasync_aux(-1, file, 0); ++ if (--aux_count) ++ return 0; ++ // kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints */ ++ // kbd_write_command_w(KBD_CCMD_MOUSE_DISABLE); ++ aux_write_ack(AUX_DISABLE_DEV); /* Disable aux device */ ++ MSECR &= ~MSECR_ENA; ++ free_irq(IRQ_MSRXINT, AUX_DEV); ++ return 0; ++} ++ ++/* ++ * Install interrupt handler. ++ * Enable auxiliary device. ++ */ ++ ++static int open_aux(struct inode *inode, struct file *file) ++{ ++ if (aux_count++) { ++ return 0; ++ } ++ queue->head = queue->tail = 0; /* Flush input queue */ ++ /* Don't enable the mouse controller until we've registered IRQ handler */ ++ if (request_irq(IRQ_MSRXINT, mouse_interrupt, SA_SHIRQ, "PS/2 Mouse", AUX_DEV)) { ++ aux_count--; ++ return -EBUSY; ++ } ++ MSECLKDIV = 0; ++ MSEPRECNT = 127; ++ MSECR &= ~MSECR_ENA; ++ mdelay(50); ++ MSECR = MSECR_ENA; ++ mdelay(50); ++ MSEDATA = 0xf4; ++ mdelay(50); ++ if (MSESTAT & 0x0100) { ++ MSESTAT = 0x0100; /* clear IRQ status */ ++ } ++/* kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); *//* Enable the ++ auxiliary port on ++ controller. */ ++ aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */ ++ // kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */ ++ ++ // send_data(KBD_CMD_ENABLE); /* try to workaround toshiba4030cdt problem */ ++ ++ return 0; ++} ++ ++/* ++ * Put bytes from input queue to buffer. ++ */ ++ ++static ssize_t ++read_aux(struct file *file, char *buffer, size_t count, loff_t * ppos) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ ssize_t i = count; ++ unsigned char c; ++ ++ if (queue_empty()) { ++ if (file->f_flags & O_NONBLOCK) ++ return -EAGAIN; ++ add_wait_queue(&queue->proc_list, &wait); ++ repeat: ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (queue_empty() && !signal_pending(current)) { ++ schedule(); ++ goto repeat; ++ } ++ current->state = TASK_RUNNING; ++ remove_wait_queue(&queue->proc_list, &wait); ++ } ++ while (i > 0 && !queue_empty()) { ++ c = get_from_queue(); ++ put_user(c, buffer++); ++ i--; ++ } ++ if (count - i) { ++ file->f_dentry->d_inode->i_atime = CURRENT_TIME; ++ return count - i; ++ } ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ return 0; ++} ++ ++/* ++ * Write to the aux device. ++ */ ++ ++static ssize_t ++write_aux(struct file *file, const char *buffer, size_t count, ++ loff_t * ppos) ++{ ++ ssize_t retval = 0; ++ ++ if (count) { ++ ssize_t written = 0; ++ ++ if (count > 32) ++ count = 32; /* Limit to 32 bytes. */ ++ do { ++ char c; ++ get_user(c, buffer++); ++ aux_write_dev(c); ++ written++; ++ } ++ while (--count); ++ retval = -EIO; ++ if (written) { ++ retval = written; ++ file->f_dentry->d_inode->i_mtime = CURRENT_TIME; ++ } ++ } ++ ++ return retval; ++} ++ ++static unsigned int aux_poll(struct file *file, poll_table * wait) ++{ ++ poll_wait(file, &queue->proc_list, wait); ++ if (!queue_empty()) ++ return POLLIN | POLLRDNORM; ++ return 0; ++} ++ ++struct file_operations psaux_fops = { ++ read: read_aux, ++ write: write_aux, ++ poll: aux_poll, ++ open: open_aux, ++ release: release_aux, ++ fasync: fasync_aux, ++}; ++ ++/* ++ * Initialize driver. ++ */ ++static struct miscdevice psaux_mouse = { ++ PSMOUSE_MINOR, "psaux", &psaux_fops ++}; ++ ++ ++static int __init psaux_init(void) ++{ ++ int ret; ++ ++ if (!request_mem_region(_MSECR, 512, "psaux")) ++ return -EBUSY; ++ ++ if (!detect_auxiliary_port()) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ misc_register(&psaux_mouse); ++ queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); ++ memset(queue, 0, sizeof(*queue)); ++ queue->head = queue->tail = 0; ++ init_waitqueue_head(&queue->proc_list); ++ ++#ifdef CONFIG_PSMOUSE ++ aux_write_ack(AUX_SET_SAMPLE); ++ aux_write_ack(100); /* 100 samples/sec */ ++ aux_write_ack(AUX_SET_RES); ++ aux_write_ack(3); /* 8 counts per mm */ ++ aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */ ++#endif ++ ret = 0; ++ ++ out: ++ if (ret) ++ release_mem_region(_MSECR, 512); ++ return ret; ++} ++ ++#endif /* CONFIG_PSMOUSE */ +diff -urN linux-2.4.26/drivers/char/serial.c linux-2.4.26-vrs1/drivers/char/serial.c +--- linux-2.4.26/drivers/char/serial.c 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/char/serial.c 2004-02-23 13:36:30.000000000 +0000 +@@ -4527,6 +4527,14 @@ + } + + /* ++ * If there is exactly one port of 8 bytes, use it. ++ */ ++ if (num_port == 1 && pci_resource_len(dev, first_port) == 8) { ++ board->flags = first_port; ++ return 0; ++ } ++ ++ /* + * If there is 1 or 0 iomem regions, and exactly one port, use + * it. + */ +diff -urN linux-2.4.26/drivers/char/serial_21285.c linux-2.4.26-vrs1/drivers/char/serial_21285.c +--- linux-2.4.26/drivers/char/serial_21285.c 2002-08-03 01:39:43.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/serial_21285.c 1970-01-01 01:00:00.000000000 +0100 +@@ -1,498 +0,0 @@ +-/* +- * linux/drivers/char/serial_21285.c +- * +- * Driver for the serial port on the 21285 StrongArm-110 core logic chip. +- * +- * Based on drivers/char/serial.c +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +- +-#define BAUD_BASE (mem_fclk_21285/64) +- +-#define SERIAL_21285_NAME "ttyFB" +-#define SERIAL_21285_MAJOR 204 +-#define SERIAL_21285_MINOR 4 +- +-#define SERIAL_21285_AUXNAME "cuafb" +-#define SERIAL_21285_AUXMAJOR 205 +-#define SERIAL_21285_AUXMINOR 4 +- +-static struct tty_driver rs285_driver, callout_driver; +-static int rs285_refcount; +-static struct tty_struct *rs285_table[1]; +- +-static struct termios *rs285_termios[1]; +-static struct termios *rs285_termios_locked[1]; +- +-static char wbuf[1000], *putp = wbuf, *getp = wbuf, x_char; +-static struct tty_struct *rs285_tty; +-static int rs285_use_count; +- +-static int rs285_write_room(struct tty_struct *tty) +-{ +- return putp >= getp ? (sizeof(wbuf) - (long) putp + (long) getp) : ((long) getp - (long) putp - 1); +-} +- +-static void rs285_rx_int(int irq, void *dev_id, struct pt_regs *regs) +-{ +- if (!rs285_tty) { +- disable_irq(IRQ_CONRX); +- return; +- } +- while (!(*CSR_UARTFLG & 0x10)) { +- int ch, flag; +- ch = *CSR_UARTDR; +- flag = *CSR_RXSTAT; +- if (flag & 4) +- tty_insert_flip_char(rs285_tty, 0, TTY_OVERRUN); +- if (flag & 2) +- flag = TTY_PARITY; +- else if (flag & 1) +- flag = TTY_FRAME; +- tty_insert_flip_char(rs285_tty, ch, flag); +- } +- tty_flip_buffer_push(rs285_tty); +-} +- +-static void rs285_send_xchar(struct tty_struct *tty, char ch) +-{ +- x_char = ch; +- enable_irq(IRQ_CONTX); +-} +- +-static void rs285_throttle(struct tty_struct *tty) +-{ +- if (I_IXOFF(tty)) +- rs285_send_xchar(tty, STOP_CHAR(tty)); +-} +- +-static void rs285_unthrottle(struct tty_struct *tty) +-{ +- if (I_IXOFF(tty)) { +- if (x_char) +- x_char = 0; +- else +- rs285_send_xchar(tty, START_CHAR(tty)); +- } +-} +- +-static void rs285_tx_int(int irq, void *dev_id, struct pt_regs *regs) +-{ +- while (!(*CSR_UARTFLG & 0x20)) { +- if (x_char) { +- *CSR_UARTDR = x_char; +- x_char = 0; +- continue; +- } +- if (putp == getp) { +- disable_irq(IRQ_CONTX); +- break; +- } +- *CSR_UARTDR = *getp; +- if (++getp >= wbuf + sizeof(wbuf)) +- getp = wbuf; +- } +- if (rs285_tty) +- wake_up_interruptible(&rs285_tty->write_wait); +-} +- +-static inline int rs285_xmit(int ch) +-{ +- if (putp + 1 == getp || (putp + 1 == wbuf + sizeof(wbuf) && getp == wbuf)) +- return 0; +- *putp = ch; +- if (++putp >= wbuf + sizeof(wbuf)) +- putp = wbuf; +- enable_irq(IRQ_CONTX); +- return 1; +-} +- +-static int rs285_write(struct tty_struct *tty, int from_user, +- const u_char * buf, int count) +-{ +- int i; +- +- if (from_user && verify_area(VERIFY_READ, buf, count)) +- return -EINVAL; +- +- for (i = 0; i < count; i++) { +- char ch; +- if (from_user) +- __get_user(ch, buf + i); +- else +- ch = buf[i]; +- if (!rs285_xmit(ch)) +- break; +- } +- return i; +-} +- +-static void rs285_put_char(struct tty_struct *tty, u_char ch) +-{ +- rs285_xmit(ch); +-} +- +-static int rs285_chars_in_buffer(struct tty_struct *tty) +-{ +- return sizeof(wbuf) - rs285_write_room(tty); +-} +- +-static void rs285_flush_buffer(struct tty_struct *tty) +-{ +- disable_irq(IRQ_CONTX); +- putp = getp = wbuf; +- if (x_char) +- enable_irq(IRQ_CONTX); +-} +- +-static inline void rs285_set_cflag(int cflag) +-{ +- int h_lcr, baud, quot; +- +- switch (cflag & CSIZE) { +- case CS5: +- h_lcr = 0x10; +- break; +- case CS6: +- h_lcr = 0x30; +- break; +- case CS7: +- h_lcr = 0x50; +- break; +- default: /* CS8 */ +- h_lcr = 0x70; +- break; +- +- } +- if (cflag & CSTOPB) +- h_lcr |= 0x08; +- if (cflag & PARENB) +- h_lcr |= 0x02; +- if (!(cflag & PARODD)) +- h_lcr |= 0x04; +- +- switch (cflag & CBAUD) { +- case B200: baud = 200; break; +- case B300: baud = 300; break; +- case B1200: baud = 1200; break; +- case B1800: baud = 1800; break; +- case B2400: baud = 2400; break; +- case B4800: baud = 4800; break; +- default: +- case B9600: baud = 9600; break; +- case B19200: baud = 19200; break; +- case B38400: baud = 38400; break; +- case B57600: baud = 57600; break; +- case B115200: baud = 115200; break; +- } +- +- /* +- * The documented expression for selecting the divisor is: +- * BAUD_BASE / baud - 1 +- * However, typically BAUD_BASE is not divisible by baud, so +- * we want to select the divisor that gives us the minimum +- * error. Therefore, we want: +- * int(BAUD_BASE / baud - 0.5) -> +- * int(BAUD_BASE / baud - (baud >> 1) / baud) -> +- * int((BAUD_BASE - (baud >> 1)) / baud) +- */ +- quot = (BAUD_BASE - (baud >> 1)) / baud; +- +- *CSR_UARTCON = 0; +- *CSR_L_UBRLCR = quot & 0xff; +- *CSR_M_UBRLCR = (quot >> 8) & 0x0f; +- *CSR_H_UBRLCR = h_lcr; +- *CSR_UARTCON = 1; +-} +- +-static void rs285_set_termios(struct tty_struct *tty, struct termios *old) +-{ +- if (old && tty->termios->c_cflag == old->c_cflag) +- return; +- rs285_set_cflag(tty->termios->c_cflag); +-} +- +- +-static void rs285_stop(struct tty_struct *tty) +-{ +- disable_irq(IRQ_CONTX); +-} +- +-static void rs285_start(struct tty_struct *tty) +-{ +- enable_irq(IRQ_CONTX); +-} +- +-static void rs285_wait_until_sent(struct tty_struct *tty, int timeout) +-{ +- int orig_jiffies = jiffies; +- while (*CSR_UARTFLG & 8) { +- current->state = TASK_INTERRUPTIBLE; +- schedule_timeout(1); +- if (signal_pending(current)) +- break; +- if (timeout && time_after(jiffies, orig_jiffies + timeout)) +- break; +- } +- current->state = TASK_RUNNING; +-} +- +-static int rs285_open(struct tty_struct *tty, struct file *filp) +-{ +- int line; +- +- MOD_INC_USE_COUNT; +- line = MINOR(tty->device) - tty->driver.minor_start; +- if (line) { +- MOD_DEC_USE_COUNT; +- return -ENODEV; +- } +- +- tty->driver_data = NULL; +- if (!rs285_tty) +- rs285_tty = tty; +- +- enable_irq(IRQ_CONRX); +- rs285_use_count++; +- return 0; +-} +- +-static void rs285_close(struct tty_struct *tty, struct file *filp) +-{ +- if (!--rs285_use_count) { +- rs285_wait_until_sent(tty, 0); +- disable_irq(IRQ_CONRX); +- disable_irq(IRQ_CONTX); +- rs285_tty = NULL; +- } +- MOD_DEC_USE_COUNT; +-} +- +-static int __init rs285_init(void) +-{ +- int baud = B9600; +- +- if (machine_is_personal_server()) +- baud = B57600; +- +- rs285_driver.magic = TTY_DRIVER_MAGIC; +- rs285_driver.driver_name = "serial_21285"; +- rs285_driver.name = SERIAL_21285_NAME; +- rs285_driver.major = SERIAL_21285_MAJOR; +- rs285_driver.minor_start = SERIAL_21285_MINOR; +- rs285_driver.num = 1; +- rs285_driver.type = TTY_DRIVER_TYPE_SERIAL; +- rs285_driver.subtype = SERIAL_TYPE_NORMAL; +- rs285_driver.init_termios = tty_std_termios; +- rs285_driver.init_termios.c_cflag = baud | CS8 | CREAD | HUPCL | CLOCAL; +- rs285_driver.flags = TTY_DRIVER_REAL_RAW; +- rs285_driver.refcount = &rs285_refcount; +- rs285_driver.table = rs285_table; +- rs285_driver.termios = rs285_termios; +- rs285_driver.termios_locked = rs285_termios_locked; +- +- rs285_driver.open = rs285_open; +- rs285_driver.close = rs285_close; +- rs285_driver.write = rs285_write; +- rs285_driver.put_char = rs285_put_char; +- rs285_driver.write_room = rs285_write_room; +- rs285_driver.chars_in_buffer = rs285_chars_in_buffer; +- rs285_driver.flush_buffer = rs285_flush_buffer; +- rs285_driver.throttle = rs285_throttle; +- rs285_driver.unthrottle = rs285_unthrottle; +- rs285_driver.send_xchar = rs285_send_xchar; +- rs285_driver.set_termios = rs285_set_termios; +- rs285_driver.stop = rs285_stop; +- rs285_driver.start = rs285_start; +- rs285_driver.wait_until_sent = rs285_wait_until_sent; +- +- callout_driver = rs285_driver; +- callout_driver.name = SERIAL_21285_AUXNAME; +- callout_driver.major = SERIAL_21285_AUXMAJOR; +- callout_driver.subtype = SERIAL_TYPE_CALLOUT; +- +- if (request_irq(IRQ_CONRX, rs285_rx_int, 0, "rs285", NULL)) +- panic("Couldn't get rx irq for rs285"); +- +- if (request_irq(IRQ_CONTX, rs285_tx_int, 0, "rs285", NULL)) +- panic("Couldn't get tx irq for rs285"); +- +- if (tty_register_driver(&rs285_driver)) +- printk(KERN_ERR "Couldn't register 21285 serial driver\n"); +- if (tty_register_driver(&callout_driver)) +- printk(KERN_ERR "Couldn't register 21285 callout driver\n"); +- +- return 0; +-} +- +-static void __exit rs285_fini(void) +-{ +- unsigned long flags; +- int ret; +- +- save_flags(flags); +- cli(); +- ret = tty_unregister_driver(&callout_driver); +- if (ret) +- printk(KERN_ERR "Unable to unregister 21285 callout driver " +- "(%d)\n", ret); +- ret = tty_unregister_driver(&rs285_driver); +- if (ret) +- printk(KERN_ERR "Unable to unregister 21285 driver (%d)\n", +- ret); +- free_irq(IRQ_CONTX, NULL); +- free_irq(IRQ_CONRX, NULL); +- restore_flags(flags); +-} +- +-module_init(rs285_init); +-module_exit(rs285_fini); +- +-#ifdef CONFIG_SERIAL_21285_CONSOLE +-/************** console driver *****************/ +- +-static void rs285_console_write(struct console *co, const char *s, u_int count) +-{ +- int i; +- +- disable_irq(IRQ_CONTX); +- for (i = 0; i < count; i++) { +- while (*CSR_UARTFLG & 0x20); +- *CSR_UARTDR = s[i]; +- if (s[i] == '\n') { +- while (*CSR_UARTFLG & 0x20); +- *CSR_UARTDR = '\r'; +- } +- } +- enable_irq(IRQ_CONTX); +-} +- +-static kdev_t rs285_console_device(struct console *c) +-{ +- return MKDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR); +-} +- +-static int __init rs285_console_setup(struct console *co, char *options) +-{ +- int baud = 9600; +- int bits = 8; +- int parity = 'n'; +- int cflag = CREAD | HUPCL | CLOCAL; +- +- if (machine_is_personal_server()) +- baud = 57600; +- +- if (options) { +- char *s = options; +- baud = simple_strtoul(options, NULL, 10); +- while (*s >= '0' && *s <= '9') +- s++; +- if (*s) +- parity = *s++; +- if (*s) +- bits = *s - '0'; +- } +- +- /* +- * Now construct a cflag setting. +- */ +- switch (baud) { +- case 1200: +- cflag |= B1200; +- break; +- case 2400: +- cflag |= B2400; +- break; +- case 4800: +- cflag |= B4800; +- break; +- case 9600: +- cflag |= B9600; +- break; +- case 19200: +- cflag |= B19200; +- break; +- case 38400: +- cflag |= B38400; +- break; +- case 57600: +- cflag |= B57600; +- break; +- case 115200: +- cflag |= B115200; +- break; +- default: +- cflag |= B9600; +- break; +- } +- switch (bits) { +- case 7: +- cflag |= CS7; +- break; +- default: +- cflag |= CS8; +- break; +- } +- switch (parity) { +- case 'o': +- case 'O': +- cflag |= PARODD; +- break; +- case 'e': +- case 'E': +- cflag |= PARENB; +- break; +- } +- co->cflag = cflag; +- rs285_set_cflag(cflag); +- rs285_console_write(NULL, "\e[2J\e[Hboot ", 12); +- if (options) +- rs285_console_write(NULL, options, strlen(options)); +- else +- rs285_console_write(NULL, "no options", 10); +- rs285_console_write(NULL, "\n", 1); +- +- return 0; +-} +- +-static struct console rs285_cons = +-{ +- name: SERIAL_21285_NAME, +- write: rs285_console_write, +- device: rs285_console_device, +- setup: rs285_console_setup, +- flags: CON_PRINTBUFFER, +- index: -1, +-}; +- +-void __init rs285_console_init(void) +-{ +- register_console(&rs285_cons); +-} +- +-#endif /* CONFIG_SERIAL_21285_CONSOLE */ +- +-MODULE_LICENSE("GPL"); +-EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/char/serial_amba.c linux-2.4.26-vrs1/drivers/char/serial_amba.c +--- linux-2.4.26/drivers/char/serial_amba.c 2002-08-03 01:39:43.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/serial_amba.c 1970-01-01 01:00:00.000000000 +0100 +@@ -1,2015 +0,0 @@ +-/* +- * linux/drivers/char/serial_amba.c +- * +- * Driver for AMBA serial ports +- * +- * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. +- * +- * Copyright 1999 ARM Limited +- * Copyright (C) 2000 Deep Blue Solutions Ltd. +- * +- * 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 +- * +- * +- * This is a generic driver for ARM AMBA-type serial ports. They +- * have a lot of 16550-like features, but are not register compatable. +- * Note that although they do have CTS, DCD and DSR inputs, they do +- * not have an RI input, nor do they have DTR or RTS outputs. If +- * required, these have to be supplied via some other means (eg, GPIO) +- * and hooked into this driver. +- * +- * This could very easily become a generic serial driver for dumb UARTs +- * (eg, {82,16x}50, 21285, SA1100). +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#define SERIAL_AMBA_NAME "ttyAM" +-#define SERIAL_AMBA_MAJOR 204 +-#define SERIAL_AMBA_MINOR 16 +-#define SERIAL_AMBA_NR 2 +- +-#define CALLOUT_AMBA_NAME "cuaam" +-#define CALLOUT_AMBA_MAJOR 205 +-#define CALLOUT_AMBA_MINOR 16 +-#define CALLOUT_AMBA_NR SERIAL_AMBA_NR +- +-#ifndef TRUE +-#define TRUE 1 +-#endif +-#ifndef FALSE +-#define FALSE 0 +-#endif +- +-#define DEBUG 0 +-#define DEBUG_LEDS 0 +- +-#if DEBUG_LEDS +-extern int get_leds(void); +-extern int set_leds(int); +-#endif +- +-/* +- * Access routines for the AMBA UARTs +- */ +-#define UART_GET_INT_STATUS(p) IO_READ((p)->uart_base + AMBA_UARTIIR) +-#define UART_GET_FR(p) IO_READ((p)->uart_base + AMBA_UARTFR) +-#define UART_GET_CHAR(p) IO_READ((p)->uart_base + AMBA_UARTDR) +-#define UART_PUT_CHAR(p, c) IO_WRITE((p)->uart_base + AMBA_UARTDR, (c)) +-#define UART_GET_RSR(p) IO_READ((p)->uart_base + AMBA_UARTRSR) +-#define UART_GET_CR(p) IO_READ((p)->uart_base + AMBA_UARTCR) +-#define UART_PUT_CR(p,c) IO_WRITE((p)->uart_base + AMBA_UARTCR, (c)) +-#define UART_GET_LCRL(p) IO_READ((p)->uart_base + AMBA_UARTLCR_L) +-#define UART_PUT_LCRL(p,c) IO_WRITE((p)->uart_base + AMBA_UARTLCR_L, (c)) +-#define UART_GET_LCRM(p) IO_READ((p)->uart_base + AMBA_UARTLCR_M) +-#define UART_PUT_LCRM(p,c) IO_WRITE((p)->uart_base + AMBA_UARTLCR_M, (c)) +-#define UART_GET_LCRH(p) IO_READ((p)->uart_base + AMBA_UARTLCR_H) +-#define UART_PUT_LCRH(p,c) IO_WRITE((p)->uart_base + AMBA_UARTLCR_H, (c)) +-#define UART_RX_DATA(s) (((s) & AMBA_UARTFR_RXFE) == 0) +-#define UART_TX_READY(s) (((s) & AMBA_UARTFR_TXFF) == 0) +-#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0) +- +-#define AMBA_UARTRSR_ANY (AMBA_UARTRSR_OE|AMBA_UARTRSR_BE|AMBA_UARTRSR_PE|AMBA_UARTRSR_FE) +-#define AMBA_UARTFR_MODEM_ANY (AMBA_UARTFR_DCD|AMBA_UARTFR_DSR|AMBA_UARTFR_CTS) +- +-/* +- * Things needed by tty driver +- */ +-static struct tty_driver ambanormal_driver, ambacallout_driver; +-static int ambauart_refcount; +-static struct tty_struct *ambauart_table[SERIAL_AMBA_NR]; +-static struct termios *ambauart_termios[SERIAL_AMBA_NR]; +-static struct termios *ambauart_termios_locked[SERIAL_AMBA_NR]; +- +-#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +-#define SUPPORT_SYSRQ +-#endif +- +-/* +- * Things needed internally to this driver +- */ +- +-/* +- * tmp_buf is used as a temporary buffer by serial_write. We need to +- * lock it in case the copy_from_user blocks while swapping in a page, +- * and some other program tries to do a serial write at the same time. +- * Since the lock will only come under contention when the system is +- * swapping and available memory is low, it makes sense to share one +- * buffer across all the serial ports, since it significantly saves +- * memory if large numbers of serial ports are open. +- */ +-static u_char *tmp_buf; +-static DECLARE_MUTEX(tmp_buf_sem); +- +-#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) +- +-/* number of characters left in xmit buffer before we ask for more */ +-#define WAKEUP_CHARS 256 +-#define AMBA_ISR_PASS_LIMIT 256 +- +-#define EVT_WRITE_WAKEUP 0 +- +-struct amba_icount { +- __u32 cts; +- __u32 dsr; +- __u32 rng; +- __u32 dcd; +- __u32 rx; +- __u32 tx; +- __u32 frame; +- __u32 overrun; +- __u32 parity; +- __u32 brk; +- __u32 buf_overrun; +-}; +- +-/* +- * Static information about the port +- */ +-struct amba_port { +- unsigned int uart_base; +- unsigned int irq; +- unsigned int uartclk; +- unsigned int fifosize; +- unsigned int tiocm_support; +- void (*set_mctrl)(struct amba_port *, u_int mctrl); +-}; +- +-/* +- * This is the state information which is persistent across opens +- */ +-struct amba_state { +- struct amba_icount icount; +- unsigned int line; +- unsigned int close_delay; +- unsigned int closing_wait; +- unsigned int custom_divisor; +- unsigned int flags; +- struct termios normal_termios; +- struct termios callout_termios; +- +- int count; +- struct amba_info *info; +-}; +- +-#define AMBA_XMIT_SIZE 1024 +-/* +- * This is the state information which is only valid when the port is open. +- */ +-struct amba_info { +- struct amba_port *port; +- struct amba_state *state; +- struct tty_struct *tty; +- unsigned char x_char; +- unsigned char old_status; +- unsigned char read_status_mask; +- unsigned char ignore_status_mask; +- struct circ_buf xmit; +- unsigned int flags; +-#ifdef SUPPORT_SYSRQ +- unsigned long sysrq; +-#endif +- +- unsigned int event; +- unsigned int timeout; +- unsigned int lcr_h; +- unsigned int mctrl; +- int blocked_open; +- pid_t session; +- pid_t pgrp; +- +- struct tasklet_struct tlet; +- +- wait_queue_head_t open_wait; +- wait_queue_head_t close_wait; +- wait_queue_head_t delta_msr_wait; +-}; +- +-#ifdef CONFIG_SERIAL_AMBA_CONSOLE +-static struct console ambauart_cons; +-#endif +-static void ambauart_change_speed(struct amba_info *info, struct termios *old_termios); +-static void ambauart_wait_until_sent(struct tty_struct *tty, int timeout); +- +-#if 1 //def CONFIG_SERIAL_INTEGRATOR +-static void amba_set_mctrl_null(struct amba_port *port, u_int mctrl) +-{ +-} +- +-static struct amba_port amba_ports[SERIAL_AMBA_NR] = { +- { +- uart_base: IO_ADDRESS(INTEGRATOR_UART0_BASE), +- irq: IRQ_UARTINT0, +- uartclk: 14745600, +- fifosize: 8, +- set_mctrl: amba_set_mctrl_null, +- }, +- { +- uart_base: IO_ADDRESS(INTEGRATOR_UART1_BASE), +- irq: IRQ_UARTINT1, +- uartclk: 14745600, +- fifosize: 8, +- set_mctrl: amba_set_mctrl_null, +- } +-}; +-#endif +- +-static struct amba_state amba_state[SERIAL_AMBA_NR]; +- +-static void ambauart_enable_rx_interrupt(struct amba_info *info) +-{ +- unsigned int cr; +- +- cr = UART_GET_CR(info->port); +- cr |= AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE; +- UART_PUT_CR(info->port, cr); +-} +- +-static void ambauart_disable_rx_interrupt(struct amba_info *info) +-{ +- unsigned int cr; +- +- cr = UART_GET_CR(info->port); +- cr &= ~(AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE); +- UART_PUT_CR(info->port, cr); +-} +- +-static void ambauart_enable_tx_interrupt(struct amba_info *info) +-{ +- unsigned int cr; +- +- cr = UART_GET_CR(info->port); +- cr |= AMBA_UARTCR_TIE; +- UART_PUT_CR(info->port, cr); +-} +- +-static void ambauart_disable_tx_interrupt(struct amba_info *info) +-{ +- unsigned int cr; +- +- cr = UART_GET_CR(info->port); +- cr &= ~AMBA_UARTCR_TIE; +- UART_PUT_CR(info->port, cr); +-} +- +-static void ambauart_stop(struct tty_struct *tty) +-{ +- struct amba_info *info = tty->driver_data; +- unsigned long flags; +- +- save_flags(flags); cli(); +- ambauart_disable_tx_interrupt(info); +- restore_flags(flags); +-} +- +-static void ambauart_start(struct tty_struct *tty) +-{ +- struct amba_info *info = tty->driver_data; +- unsigned long flags; +- +- save_flags(flags); cli(); +- if (info->xmit.head != info->xmit.tail +- && info->xmit.buf) +- ambauart_enable_tx_interrupt(info); +- restore_flags(flags); +-} +- +- +-/* +- * This routine is used by the interrupt handler to schedule +- * processing in the software interrupt portion of the driver. +- */ +-static void ambauart_event(struct amba_info *info, int event) +-{ +- info->event |= 1 << event; +- tasklet_schedule(&info->tlet); +-} +- +-static void +-#ifdef SUPPORT_SYSRQ +-ambauart_rx_chars(struct amba_info *info, struct pt_regs *regs) +-#else +-ambauart_rx_chars(struct amba_info *info) +-#endif +-{ +- struct tty_struct *tty = info->tty; +- unsigned int status, ch, rsr, flg, ignored = 0; +- struct amba_icount *icount = &info->state->icount; +- struct amba_port *port = info->port; +- +- status = UART_GET_FR(port); +- while (UART_RX_DATA(status)) { +- ch = UART_GET_CHAR(port); +- +- if (tty->flip.count >= TTY_FLIPBUF_SIZE) +- goto ignore_char; +- icount->rx++; +- +- flg = TTY_NORMAL; +- +- /* +- * Note that the error handling code is +- * out of the main execution path +- */ +- rsr = UART_GET_RSR(port); +- if (rsr & AMBA_UARTRSR_ANY) +- goto handle_error; +-#ifdef SUPPORT_SYSRQ +- if (info->sysrq) { +- if (ch && time_before(jiffies, info->sysrq)) { +- handle_sysrq(ch, regs, NULL, NULL); +- info->sysrq = 0; +- goto ignore_char; +- } +- info->sysrq = 0; +- } +-#endif +- error_return: +- *tty->flip.flag_buf_ptr++ = flg; +- *tty->flip.char_buf_ptr++ = ch; +- tty->flip.count++; +- ignore_char: +- status = UART_GET_FR(port); +- } +-out: +- tty_flip_buffer_push(tty); +- return; +- +-handle_error: +- if (rsr & AMBA_UARTRSR_BE) { +- rsr &= ~(AMBA_UARTRSR_FE | AMBA_UARTRSR_PE); +- icount->brk++; +- +-#ifdef SUPPORT_SYSRQ +- if (info->state->line == ambauart_cons.index) { +- if (!info->sysrq) { +- info->sysrq = jiffies + HZ*5; +- goto ignore_char; +- } +- } +-#endif +- } else if (rsr & AMBA_UARTRSR_PE) +- icount->parity++; +- else if (rsr & AMBA_UARTRSR_FE) +- icount->frame++; +- if (rsr & AMBA_UARTRSR_OE) +- icount->overrun++; +- +- if (rsr & info->ignore_status_mask) { +- if (++ignored > 100) +- goto out; +- goto ignore_char; +- } +- rsr &= info->read_status_mask; +- +- if (rsr & AMBA_UARTRSR_BE) +- flg = TTY_BREAK; +- else if (rsr & AMBA_UARTRSR_PE) +- flg = TTY_PARITY; +- else if (rsr & AMBA_UARTRSR_FE) +- flg = TTY_FRAME; +- +- if (rsr & AMBA_UARTRSR_OE) { +- /* +- * CHECK: does overrun affect the current character? +- * ASSUMPTION: it does not. +- */ +- *tty->flip.flag_buf_ptr++ = flg; +- *tty->flip.char_buf_ptr++ = ch; +- tty->flip.count++; +- if (tty->flip.count >= TTY_FLIPBUF_SIZE) +- goto ignore_char; +- ch = 0; +- flg = TTY_OVERRUN; +- } +-#ifdef SUPPORT_SYSRQ +- info->sysrq = 0; +-#endif +- goto error_return; +-} +- +-static void ambauart_tx_chars(struct amba_info *info) +-{ +- struct amba_port *port = info->port; +- int count; +- +- if (info->x_char) { +- UART_PUT_CHAR(port, info->x_char); +- info->state->icount.tx++; +- info->x_char = 0; +- return; +- } +- if (info->xmit.head == info->xmit.tail +- || info->tty->stopped +- || info->tty->hw_stopped) { +- ambauart_disable_tx_interrupt(info); +- return; +- } +- +- count = port->fifosize; +- do { +- UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]); +- info->xmit.tail = (info->xmit.tail + 1) & (AMBA_XMIT_SIZE - 1); +- info->state->icount.tx++; +- if (info->xmit.head == info->xmit.tail) +- break; +- } while (--count > 0); +- +- if (CIRC_CNT(info->xmit.head, +- info->xmit.tail, +- AMBA_XMIT_SIZE) < WAKEUP_CHARS) +- ambauart_event(info, EVT_WRITE_WAKEUP); +- +- if (info->xmit.head == info->xmit.tail) { +- ambauart_disable_tx_interrupt(info); +- } +-} +- +-static void ambauart_modem_status(struct amba_info *info) +-{ +- unsigned int status, delta; +- struct amba_icount *icount = &info->state->icount; +- +- status = UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY; +- +- delta = status ^ info->old_status; +- info->old_status = status; +- +- if (!delta) +- return; +- +- if (delta & AMBA_UARTFR_DCD) { +- icount->dcd++; +-#ifdef CONFIG_HARD_PPS +- if ((info->flags & ASYNC_HARDPPS_CD) && +- (status & AMBA_UARTFR_DCD) +- hardpps(); +-#endif +- if (info->flags & ASYNC_CHECK_CD) { +- if (status & AMBA_UARTFR_DCD) +- wake_up_interruptible(&info->open_wait); +- else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && +- (info->flags & ASYNC_CALLOUT_NOHUP))) { +- if (info->tty) +- tty_hangup(info->tty); +- } +- } +- } +- +- if (delta & AMBA_UARTFR_DSR) +- icount->dsr++; +- +- if (delta & AMBA_UARTFR_CTS) { +- icount->cts++; +- +- if (info->flags & ASYNC_CTS_FLOW) { +- status &= AMBA_UARTFR_CTS; +- +- if (info->tty->hw_stopped) { +- if (status) { +- info->tty->hw_stopped = 0; +- ambauart_enable_tx_interrupt(info); +- ambauart_event(info, EVT_WRITE_WAKEUP); +- } +- } else { +- if (!status) { +- info->tty->hw_stopped = 1; +- ambauart_disable_tx_interrupt(info); +- } +- } +- } +- } +- wake_up_interruptible(&info->delta_msr_wait); +- +-} +- +-static void ambauart_int(int irq, void *dev_id, struct pt_regs *regs) +-{ +- struct amba_info *info = dev_id; +- unsigned int status, pass_counter = 0; +- +-#if DEBUG_LEDS +- // tell the world +- set_leds(get_leds() | RED_LED); +-#endif +- +- status = UART_GET_INT_STATUS(info->port); +- do { +- /* +- * FIXME: what about clearing the interrupts? +- */ +- +- if (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS)) +-#ifdef SUPPORT_SYSRQ +- ambauart_rx_chars(info, regs); +-#else +- ambauart_rx_chars(info); +-#endif +- if (status & AMBA_UARTIIR_TIS) +- ambauart_tx_chars(info); +- if (status & AMBA_UARTIIR_MIS) +- ambauart_modem_status(info); +- if (pass_counter++ > AMBA_ISR_PASS_LIMIT) +- break; +- +- status = UART_GET_INT_STATUS(info->port); +- } while (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS | AMBA_UARTIIR_TIS)); +- +-#if DEBUG_LEDS +- // tell the world +- set_leds(get_leds() & ~RED_LED); +-#endif +-} +- +-static void ambauart_tasklet_action(unsigned long data) +-{ +- struct amba_info *info = (struct amba_info *)data; +- struct tty_struct *tty; +- +- tty = info->tty; +- if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event)) +- return; +- +- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && +- tty->ldisc.write_wakeup) +- (tty->ldisc.write_wakeup)(tty); +- wake_up_interruptible(&tty->write_wait); +-} +- +-static int ambauart_startup(struct amba_info *info) +-{ +- unsigned long flags; +- unsigned long page; +- int retval = 0; +- +- page = get_zeroed_page(GFP_KERNEL); +- if (!page) +- return -ENOMEM; +- +- save_flags(flags); cli(); +- +- if (info->flags & ASYNC_INITIALIZED) { +- free_page(page); +- goto errout; +- } +- +- if (info->xmit.buf) +- free_page(page); +- else +- info->xmit.buf = (unsigned char *) page; +- +- /* +- * Allocate the IRQ +- */ +- retval = request_irq(info->port->irq, ambauart_int, 0, "amba", info); +- if (retval) { +- if (capable(CAP_SYS_ADMIN)) { +- if (info->tty) +- set_bit(TTY_IO_ERROR, &info->tty->flags); +- retval = 0; +- } +- goto errout; +- } +- +- info->mctrl = 0; +- if (info->tty->termios->c_cflag & CBAUD) +- info->mctrl = TIOCM_RTS | TIOCM_DTR; +- info->port->set_mctrl(info->port, info->mctrl); +- +- /* +- * initialise the old status of the modem signals +- */ +- info->old_status = UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY; +- +- /* +- * Finally, enable interrupts +- */ +- ambauart_enable_rx_interrupt(info); +- +- if (info->tty) +- clear_bit(TTY_IO_ERROR, &info->tty->flags); +- info->xmit.head = info->xmit.tail = 0; +- +- /* +- * Set up the tty->alt_speed kludge +- */ +- if (info->tty) { +- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) +- info->tty->alt_speed = 57600; +- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) +- info->tty->alt_speed = 115200; +- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) +- info->tty->alt_speed = 230400; +- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) +- info->tty->alt_speed = 460800; +- } +- +- /* +- * and set the speed of the serial port +- */ +- ambauart_change_speed(info, 0); +- +- info->flags |= ASYNC_INITIALIZED; +- restore_flags(flags); +- return 0; +- +-errout: +- restore_flags(flags); +- return retval; +-} +- +-/* +- * This routine will shutdown a serial port; interrupts are disabled, and +- * DTR is dropped if the hangup on close termio flag is on. +- */ +-static void ambauart_shutdown(struct amba_info *info) +-{ +- unsigned long flags; +- +- if (!(info->flags & ASYNC_INITIALIZED)) +- return; +- +- save_flags(flags); cli(); /* Disable interrupts */ +- +- /* +- * clear delta_msr_wait queue to avoid mem leaks: we may free the irq +- * here so the queue might never be woken up +- */ +- wake_up_interruptible(&info->delta_msr_wait); +- +- /* +- * Free the IRQ +- */ +- free_irq(info->port->irq, info); +- +- if (info->xmit.buf) { +- unsigned long pg = (unsigned long) info->xmit.buf; +- info->xmit.buf = NULL; +- free_page(pg); +- } +- +- /* +- * disable all interrupts, disable the port +- */ +- UART_PUT_CR(info->port, 0); +- +- /* disable break condition and fifos */ +- UART_PUT_LCRH(info->port, UART_GET_LCRH(info->port) & +- ~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN)); +- +- if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) +- info->mctrl &= ~(TIOCM_DTR|TIOCM_RTS); +- info->port->set_mctrl(info->port, info->mctrl); +- +- /* kill off our tasklet */ +- tasklet_kill(&info->tlet); +- if (info->tty) +- set_bit(TTY_IO_ERROR, &info->tty->flags); +- +- info->flags &= ~ASYNC_INITIALIZED; +- restore_flags(flags); +-} +- +-static void ambauart_change_speed(struct amba_info *info, struct termios *old_termios) +-{ +- unsigned int lcr_h, baud, quot, cflag, old_cr, bits; +- unsigned long flags; +- +- if (!info->tty || !info->tty->termios) +- return; +- +- cflag = info->tty->termios->c_cflag; +- +-#if DEBUG +- printk("ambauart_set_cflag(0x%x) called\n", cflag); +-#endif +- /* byte size and parity */ +- switch (cflag & CSIZE) { +- case CS5: lcr_h = AMBA_UARTLCR_H_WLEN_5; bits = 7; break; +- case CS6: lcr_h = AMBA_UARTLCR_H_WLEN_6; bits = 8; break; +- case CS7: lcr_h = AMBA_UARTLCR_H_WLEN_7; bits = 9; break; +- default: lcr_h = AMBA_UARTLCR_H_WLEN_8; bits = 10; break; // CS8 +- } +- if (cflag & CSTOPB) { +- lcr_h |= AMBA_UARTLCR_H_STP2; +- bits ++; +- } +- if (cflag & PARENB) { +- lcr_h |= AMBA_UARTLCR_H_PEN; +- bits++; +- if (!(cflag & PARODD)) +- lcr_h |= AMBA_UARTLCR_H_EPS; +- } +- if (info->port->fifosize > 1) +- lcr_h |= AMBA_UARTLCR_H_FEN; +- +- do { +- /* Determine divisor based on baud rate */ +- baud = tty_get_baud_rate(info->tty); +- if (!baud) +- baud = 9600; +- +- if (baud == 38400 && +- ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) +- quot = info->state->custom_divisor; +- else +- quot = (info->port->uartclk / (16 * baud)) - 1; +- +- if (!quot && old_termios) { +- info->tty->termios->c_cflag &= ~CBAUD; +- info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); +- old_termios = NULL; +- } +- } while (quot == 0 && old_termios); +- +- /* As a last resort, if the quotient is zero, default to 9600 bps */ +- if (!quot) +- quot = (info->port->uartclk / (16 * 9600)) - 1; +- +- info->timeout = (info->port->fifosize * HZ * bits * quot) / +- (info->port->uartclk / 16); +- info->timeout += HZ/50; /* Add .02 seconds of slop */ +- +- if (cflag & CRTSCTS) +- info->flags |= ASYNC_CTS_FLOW; +- else +- info->flags &= ~ASYNC_CTS_FLOW; +- if (cflag & CLOCAL) +- info->flags &= ~ASYNC_CHECK_CD; +- else +- info->flags |= ASYNC_CHECK_CD; +- +- /* +- * Set up parity check flag +- */ +-#define RELEVENT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) +- +- info->read_status_mask = AMBA_UARTRSR_OE; +- if (I_INPCK(info->tty)) +- info->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; +- if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) +- info->read_status_mask |= AMBA_UARTRSR_BE; +- +- /* +- * Characters to ignore +- */ +- info->ignore_status_mask = 0; +- if (I_IGNPAR(info->tty)) +- info->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; +- if (I_IGNBRK(info->tty)) { +- info->ignore_status_mask |= AMBA_UARTRSR_BE; +- /* +- * If we're ignoring parity and break indicators, +- * ignore overruns to (for real raw support). +- */ +- if (I_IGNPAR(info->tty)) +- info->ignore_status_mask |= AMBA_UARTRSR_OE; +- } +- +- /* first, disable everything */ +- save_flags(flags); cli(); +- old_cr = UART_GET_CR(info->port) &= ~AMBA_UARTCR_MSIE; +- +- if ((info->flags & ASYNC_HARDPPS_CD) || +- (cflag & CRTSCTS) || +- !(cflag & CLOCAL)) +- old_cr |= AMBA_UARTCR_MSIE; +- +- UART_PUT_CR(info->port, 0); +- restore_flags(flags); +- +- /* Set baud rate */ +- UART_PUT_LCRM(info->port, ((quot & 0xf00) >> 8)); +- UART_PUT_LCRL(info->port, (quot & 0xff)); +- +- /* +- * ----------v----------v----------v----------v----- +- * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L +- * ----------^----------^----------^----------^----- +- */ +- UART_PUT_LCRH(info->port, lcr_h); +- UART_PUT_CR(info->port, old_cr); +-} +- +-static void ambauart_put_char(struct tty_struct *tty, u_char ch) +-{ +- struct amba_info *info = tty->driver_data; +- unsigned long flags; +- +- if (!tty || !info->xmit.buf) +- return; +- +- save_flags(flags); cli(); +- if (CIRC_SPACE(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE) != 0) { +- info->xmit.buf[info->xmit.head] = ch; +- info->xmit.head = (info->xmit.head + 1) & (AMBA_XMIT_SIZE - 1); +- } +- restore_flags(flags); +-} +- +-static void ambauart_flush_chars(struct tty_struct *tty) +-{ +- struct amba_info *info = tty->driver_data; +- unsigned long flags; +- +- if (info->xmit.head == info->xmit.tail +- || tty->stopped +- || tty->hw_stopped +- || !info->xmit.buf) +- return; +- +- save_flags(flags); cli(); +- ambauart_enable_tx_interrupt(info); +- restore_flags(flags); +-} +- +-static int ambauart_write(struct tty_struct *tty, int from_user, +- const u_char * buf, int count) +-{ +- struct amba_info *info = tty->driver_data; +- unsigned long flags; +- int c, ret = 0; +- +- if (!tty || !info->xmit.buf || !tmp_buf) +- return 0; +- +- save_flags(flags); +- if (from_user) { +- down(&tmp_buf_sem); +- while (1) { +- int c1; +- c = CIRC_SPACE_TO_END(info->xmit.head, +- info->xmit.tail, +- AMBA_XMIT_SIZE); +- if (count < c) +- c = count; +- if (c <= 0) +- break; +- +- c -= copy_from_user(tmp_buf, buf, c); +- if (!c) { +- if (!ret) +- ret = -EFAULT; +- break; +- } +- cli(); +- c1 = CIRC_SPACE_TO_END(info->xmit.head, +- info->xmit.tail, +- AMBA_XMIT_SIZE); +- if (c1 < c) +- c = c1; +- memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); +- info->xmit.head = (info->xmit.head + c) & +- (AMBA_XMIT_SIZE - 1); +- restore_flags(flags); +- buf += c; +- count -= c; +- ret += c; +- } +- up(&tmp_buf_sem); +- } else { +- cli(); +- while (1) { +- c = CIRC_SPACE_TO_END(info->xmit.head, +- info->xmit.tail, +- AMBA_XMIT_SIZE); +- if (count < c) +- c = count; +- if (c <= 0) +- break; +- memcpy(info->xmit.buf + info->xmit.head, buf, c); +- info->xmit.head = (info->xmit.head + c) & +- (AMBA_XMIT_SIZE - 1); +- buf += c; +- count -= c; +- ret += c; +- } +- restore_flags(flags); +- } +- if (info->xmit.head != info->xmit.tail +- && !tty->stopped +- && !tty->hw_stopped) +- ambauart_enable_tx_interrupt(info); +- return ret; +-} +- +-static int ambauart_write_room(struct tty_struct *tty) +-{ +- struct amba_info *info = tty->driver_data; +- +- return CIRC_SPACE(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE); +-} +- +-static int ambauart_chars_in_buffer(struct tty_struct *tty) +-{ +- struct amba_info *info = tty->driver_data; +- +- return CIRC_CNT(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE); +-} +- +-static void ambauart_flush_buffer(struct tty_struct *tty) +-{ +- struct amba_info *info = tty->driver_data; +- unsigned long flags; +- +-#if DEBUG +- printk("ambauart_flush_buffer(%d) called\n", +- MINOR(tty->device) - tty->driver.minor_start); +-#endif +- save_flags(flags); cli(); +- info->xmit.head = info->xmit.tail = 0; +- restore_flags(flags); +- wake_up_interruptible(&tty->write_wait); +- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && +- tty->ldisc.write_wakeup) +- (tty->ldisc.write_wakeup)(tty); +-} +- +-/* +- * This function is used to send a high-priority XON/XOFF character to +- * the device +- */ +-static void ambauart_send_xchar(struct tty_struct *tty, char ch) +-{ +- struct amba_info *info = tty->driver_data; +- +- info->x_char = ch; +- if (ch) +- ambauart_enable_tx_interrupt(info); +-} +- +-static void ambauart_throttle(struct tty_struct *tty) +-{ +- struct amba_info *info = tty->driver_data; +- unsigned long flags; +- +- if (I_IXOFF(tty)) +- ambauart_send_xchar(tty, STOP_CHAR(tty)); +- +- if (tty->termios->c_cflag & CRTSCTS) { +- save_flags(flags); cli(); +- info->mctrl &= ~TIOCM_RTS; +- info->port->set_mctrl(info->port, info->mctrl); +- restore_flags(flags); +- } +-} +- +-static void ambauart_unthrottle(struct tty_struct *tty) +-{ +- struct amba_info *info = (struct amba_info *) tty->driver_data; +- unsigned long flags; +- +- if (I_IXOFF(tty)) { +- if (info->x_char) +- info->x_char = 0; +- else +- ambauart_send_xchar(tty, START_CHAR(tty)); +- } +- +- if (tty->termios->c_cflag & CRTSCTS) { +- save_flags(flags); cli(); +- info->mctrl |= TIOCM_RTS; +- info->port->set_mctrl(info->port, info->mctrl); +- restore_flags(flags); +- } +-} +- +-static int get_serial_info(struct amba_info *info, struct serial_struct *retinfo) +-{ +- struct amba_state *state = info->state; +- struct amba_port *port = info->port; +- struct serial_struct tmp; +- +- memset(&tmp, 0, sizeof(tmp)); +- tmp.type = 0; +- tmp.line = state->line; +- tmp.port = port->uart_base; +- if (HIGH_BITS_OFFSET) +- tmp.port_high = port->uart_base >> HIGH_BITS_OFFSET; +- tmp.irq = port->irq; +- tmp.flags = 0; +- tmp.xmit_fifo_size = port->fifosize; +- tmp.baud_base = port->uartclk / 16; +- tmp.close_delay = state->close_delay; +- tmp.closing_wait = state->closing_wait; +- tmp.custom_divisor = state->custom_divisor; +- +- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) +- return -EFAULT; +- return 0; +-} +- +-static int set_serial_info(struct amba_info *info, +- struct serial_struct *newinfo) +-{ +- struct serial_struct new_serial; +- struct amba_state *state, old_state; +- struct amba_port *port; +- unsigned long new_port; +- unsigned int i, change_irq, change_port; +- int retval = 0; +- +- if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) +- return -EFAULT; +- +- state = info->state; +- old_state = *state; +- port = info->port; +- +- new_port = new_serial.port; +- if (HIGH_BITS_OFFSET) +- new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; +- +- change_irq = new_serial.irq != port->irq; +- change_port = new_port != port->uart_base; +- +- if (!capable(CAP_SYS_ADMIN)) { +- if (change_irq || change_port || +- (new_serial.baud_base != port->uartclk / 16) || +- (new_serial.close_delay != state->close_delay) || +- (new_serial.xmit_fifo_size != port->fifosize) || +- ((new_serial.flags & ~ASYNC_USR_MASK) != +- (state->flags & ~ASYNC_USR_MASK))) +- return -EPERM; +- state->flags = ((state->flags & ~ASYNC_USR_MASK) | +- (new_serial.flags & ASYNC_USR_MASK)); +- info->flags = ((info->flags & ~ASYNC_USR_MASK) | +- (new_serial.flags & ASYNC_USR_MASK)); +- state->custom_divisor = new_serial.custom_divisor; +- goto check_and_exit; +- } +- +- if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || +- (new_serial.baud_base < 9600)) +- return -EINVAL; +- +- if (new_serial.type && change_port) { +- for (i = 0; i < SERIAL_AMBA_NR; i++) +- if ((port != amba_ports + i) && +- amba_ports[i].uart_base != new_port) +- return -EADDRINUSE; +- } +- +- if ((change_port || change_irq) && (state->count > 1)) +- return -EBUSY; +- +- /* +- * OK, past this point, all the error checking has been done. +- * At this point, we start making changes..... +- */ +- port->uartclk = new_serial.baud_base * 16; +- state->flags = ((state->flags & ~ASYNC_FLAGS) | +- (new_serial.flags & ASYNC_FLAGS)); +- info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | +- (info->flags & ASYNC_INTERNAL_FLAGS)); +- state->custom_divisor = new_serial.custom_divisor; +- state->close_delay = new_serial.close_delay * HZ / 100; +- state->closing_wait = new_serial.closing_wait * HZ / 100; +- info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +- port->fifosize = new_serial.xmit_fifo_size; +- +- if (change_port || change_irq) { +- /* +- * We need to shutdown the serial port at the old +- * port/irq combination. +- */ +- ambauart_shutdown(info); +- port->irq = new_serial.irq; +- port->uart_base = new_port; +- } +- +-check_and_exit: +- if (!port->uart_base) +- return 0; +- if (info->flags & ASYNC_INITIALIZED) { +- if ((old_state.flags & ASYNC_SPD_MASK) != +- (state->flags & ASYNC_SPD_MASK) || +- (old_state.custom_divisor != state->custom_divisor)) { +- if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) +- info->tty->alt_speed = 57600; +- if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) +- info->tty->alt_speed = 115200; +- if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) +- info->tty->alt_speed = 230400; +- if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) +- info->tty->alt_speed = 460800; +- ambauart_change_speed(info, NULL); +- } +- } else +- retval = ambauart_startup(info); +- return retval; +-} +- +- +-/* +- * get_lsr_info - get line status register info +- */ +-static int get_lsr_info(struct amba_info *info, unsigned int *value) +-{ +- unsigned int result, status; +- unsigned long flags; +- +- save_flags(flags); cli(); +- status = UART_GET_FR(info->port); +- restore_flags(flags); +- result = status & AMBA_UARTFR_BUSY ? TIOCSER_TEMT : 0; +- +- /* +- * If we're about to load something into the transmit +- * register, we'll pretend the transmitter isn't empty to +- * avoid a race condition (depending on when the transmit +- * interrupt happens). +- */ +- if (info->x_char || +- ((CIRC_CNT(info->xmit.head, info->xmit.tail, +- AMBA_XMIT_SIZE) > 0) && +- !info->tty->stopped && !info->tty->hw_stopped)) +- result &= TIOCSER_TEMT; +- +- return put_user(result, value); +-} +- +-static int get_modem_info(struct amba_info *info, unsigned int *value) +-{ +- unsigned int result = info->mctrl; +- unsigned int status; +- +- status = UART_GET_FR(info->port); +- if (status & AMBA_UARTFR_DCD) +- result |= TIOCM_CAR; +- if (status & AMBA_UARTFR_DSR) +- result |= TIOCM_DSR; +- if (status & AMBA_UARTFR_CTS) +- result |= TIOCM_CTS; +- +- return put_user(result, value); +-} +- +-static int set_modem_info(struct amba_info *info, unsigned int cmd, +- unsigned int *value) +-{ +- unsigned int arg, old; +- unsigned long flags; +- +- if (get_user(arg, value)) +- return -EFAULT; +- +- old = info->mctrl; +- switch (cmd) { +- case TIOCMBIS: +- info->mctrl |= arg; +- break; +- +- case TIOCMBIC: +- info->mctrl &= ~arg; +- break; +- +- case TIOCMSET: +- info->mctrl = arg; +- break; +- +- default: +- return -EINVAL; +- } +- save_flags(flags); cli(); +- if (old != info->mctrl) +- info->port->set_mctrl(info->port, info->mctrl); +- restore_flags(flags); +- return 0; +-} +- +-static void ambauart_break_ctl(struct tty_struct *tty, int break_state) +-{ +- struct amba_info *info = tty->driver_data; +- unsigned long flags; +- unsigned int lcr_h; +- +- save_flags(flags); cli(); +- lcr_h = UART_GET_LCRH(info->port); +- if (break_state == -1) +- lcr_h |= AMBA_UARTLCR_H_BRK; +- else +- lcr_h &= ~AMBA_UARTLCR_H_BRK; +- UART_PUT_LCRH(info->port, lcr_h); +- restore_flags(flags); +-} +- +-static int ambauart_ioctl(struct tty_struct *tty, struct file *file, +- unsigned int cmd, unsigned long arg) +-{ +- struct amba_info *info = tty->driver_data; +- struct amba_icount cprev, cnow; +- struct serial_icounter_struct icount; +- unsigned long flags; +- +- if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && +- (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && +- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { +- if (tty->flags & (1 << TTY_IO_ERROR)) +- return -EIO; +- } +- +- switch (cmd) { +- case TIOCMGET: +- return get_modem_info(info, (unsigned int *)arg); +- case TIOCMBIS: +- case TIOCMBIC: +- case TIOCMSET: +- return set_modem_info(info, cmd, (unsigned int *)arg); +- case TIOCGSERIAL: +- return get_serial_info(info, +- (struct serial_struct *)arg); +- case TIOCSSERIAL: +- return set_serial_info(info, +- (struct serial_struct *)arg); +- case TIOCSERGETLSR: /* Get line status register */ +- return get_lsr_info(info, (unsigned int *)arg); +- /* +- * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change +- * - mask passed in arg for lines of interest +- * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) +- * Caller should use TIOCGICOUNT to see which one it was +- */ +- case TIOCMIWAIT: +- save_flags(flags); cli(); +- /* note the counters on entry */ +- cprev = info->state->icount; +- /* Force modem status interrupts on */ +- UART_PUT_CR(info->port, UART_GET_CR(info->port) | AMBA_UARTCR_MSIE); +- restore_flags(flags); +- while (1) { +- interruptible_sleep_on(&info->delta_msr_wait); +- /* see if a signal did it */ +- if (signal_pending(current)) +- return -ERESTARTSYS; +- save_flags(flags); cli(); +- cnow = info->state->icount; /* atomic copy */ +- restore_flags(flags); +- if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && +- cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) +- return -EIO; /* no change => error */ +- if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || +- ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || +- ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || +- ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { +- return 0; +- } +- cprev = cnow; +- } +- /* NOTREACHED */ +- +- /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for +- * RI where only 0->1 is counted. +- */ +- case TIOCGICOUNT: +- save_flags(flags); cli(); +- cnow = info->state->icount; +- restore_flags(flags); +- icount.cts = cnow.cts; +- icount.dsr = cnow.dsr; +- icount.rng = cnow.rng; +- icount.dcd = cnow.dcd; +- icount.rx = cnow.rx; +- icount.tx = cnow.tx; +- icount.frame = cnow.frame; +- icount.overrun = cnow.overrun; +- icount.parity = cnow.parity; +- icount.brk = cnow.brk; +- icount.buf_overrun = cnow.buf_overrun; +- +- return copy_to_user((void *)arg, &icount, sizeof(icount)) +- ? -EFAULT : 0; +- +- default: +- return -ENOIOCTLCMD; +- } +- return 0; +-} +- +-static void ambauart_set_termios(struct tty_struct *tty, struct termios *old_termios) +-{ +- struct amba_info *info = tty->driver_data; +- unsigned long flags; +- unsigned int cflag = tty->termios->c_cflag; +- +- if ((cflag ^ old_termios->c_cflag) == 0 && +- RELEVENT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) +- return; +- +- ambauart_change_speed(info, old_termios); +- +- /* Handle transition to B0 status */ +- if ((old_termios->c_cflag & CBAUD) && +- !(cflag & CBAUD)) { +- save_flags(flags); cli(); +- info->mctrl &= ~(TIOCM_RTS | TIOCM_DTR); +- info->port->set_mctrl(info->port, info->mctrl); +- restore_flags(flags); +- } +- +- /* Handle transition away from B0 status */ +- if (!(old_termios->c_cflag & CBAUD) && +- (cflag & CBAUD)) { +- save_flags(flags); cli(); +- info->mctrl |= TIOCM_DTR; +- if (!(cflag & CRTSCTS) || +- !test_bit(TTY_THROTTLED, &tty->flags)) +- info->mctrl |= TIOCM_RTS; +- info->port->set_mctrl(info->port, info->mctrl); +- restore_flags(flags); +- } +- +- /* Handle turning off CRTSCTS */ +- if ((old_termios->c_cflag & CRTSCTS) && +- !(cflag & CRTSCTS)) { +- tty->hw_stopped = 0; +- ambauart_start(tty); +- } +- +-#if 0 +- /* +- * No need to wake up processes in open wait, since they +- * sample the CLOCAL flag once, and don't recheck it. +- * XXX It's not clear whether the current behavior is correct +- * or not. Hence, this may change..... +- */ +- if (!(old_termios->c_cflag & CLOCAL) && +- (tty->termios->c_cflag & CLOCAL)) +- wake_up_interruptible(&info->open_wait); +-#endif +-} +- +-static void ambauart_close(struct tty_struct *tty, struct file *filp) +-{ +- struct amba_info *info = tty->driver_data; +- struct amba_state *state; +- unsigned long flags; +- +- if (!info) +- return; +- +- state = info->state; +- +-#if DEBUG +- printk("ambauart_close() called\n"); +-#endif +- +- save_flags(flags); cli(); +- +- if (tty_hung_up_p(filp)) { +- MOD_DEC_USE_COUNT; +- restore_flags(flags); +- return; +- } +- +- if ((tty->count == 1) && (state->count != 1)) { +- /* +- * Uh, oh. tty->count is 1, which means that the tty +- * structure will be freed. state->count should always +- * be one in these conditions. If it's greater than +- * one, we've got real problems, since it means the +- * serial port won't be shutdown. +- */ +- printk("ambauart_close: bad serial port count; tty->count is 1, " +- "state->count is %d\n", state->count); +- state->count = 1; +- } +- if (--state->count < 0) { +- printk("rs_close: bad serial port count for %s%d: %d\n", +- tty->driver.name, info->state->line, state->count); +- state->count = 0; +- } +- if (state->count) { +- MOD_DEC_USE_COUNT; +- restore_flags(flags); +- return; +- } +- info->flags |= ASYNC_CLOSING; +- restore_flags(flags); +- /* +- * Save the termios structure, since this port may have +- * separate termios for callout and dialin. +- */ +- if (info->flags & ASYNC_NORMAL_ACTIVE) +- info->state->normal_termios = *tty->termios; +- if (info->flags & ASYNC_CALLOUT_ACTIVE) +- info->state->callout_termios = *tty->termios; +- /* +- * Now we wait for the transmit buffer to clear; and we notify +- * the line discipline to only process XON/XOFF characters. +- */ +- tty->closing = 1; +- if (info->state->closing_wait != ASYNC_CLOSING_WAIT_NONE) +- tty_wait_until_sent(tty, info->state->closing_wait); +- /* +- * At this point, we stop accepting input. To do this, we +- * disable the receive line status interrupts. +- */ +- if (info->flags & ASYNC_INITIALIZED) { +- ambauart_disable_rx_interrupt(info); +- /* +- * Before we drop DTR, make sure the UART transmitter +- * has completely drained; this is especially +- * important if there is a transmit FIFO! +- */ +- ambauart_wait_until_sent(tty, info->timeout); +- } +- ambauart_shutdown(info); +- if (tty->driver.flush_buffer) +- tty->driver.flush_buffer(tty); +- if (tty->ldisc.flush_buffer) +- tty->ldisc.flush_buffer(tty); +- tty->closing = 0; +- info->event = 0; +- info->tty = NULL; +- if (info->blocked_open) { +- if (info->state->close_delay) { +- set_current_state(TASK_INTERRUPTIBLE); +- schedule_timeout(info->state->close_delay); +- } +- wake_up_interruptible(&info->open_wait); +- } +- info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| +- ASYNC_CLOSING); +- wake_up_interruptible(&info->close_wait); +- MOD_DEC_USE_COUNT; +-} +- +-static void ambauart_wait_until_sent(struct tty_struct *tty, int timeout) +-{ +- struct amba_info *info = (struct amba_info *) tty->driver_data; +- unsigned long char_time, expire; +- unsigned int status; +- +- if (info->port->fifosize == 0) +- return; +- +- /* +- * Set the check interval to be 1/5 of the estimated time to +- * send a single character, and make it at least 1. The check +- * interval should also be less than the timeout. +- * +- * Note: we have to use pretty tight timings here to satisfy +- * the NIST-PCTS. +- */ +- char_time = (info->timeout - HZ/50) / info->port->fifosize; +- char_time = char_time / 5; +- if (char_time == 0) +- char_time = 1; +- if (timeout && timeout < char_time) +- char_time = timeout; +- /* +- * If the transmitter hasn't cleared in twice the approximate +- * amount of time to send the entire FIFO, it probably won't +- * ever clear. This assumes the UART isn't doing flow +- * control, which is currently the case. Hence, if it ever +- * takes longer than info->timeout, this is probably due to a +- * UART bug of some kind. So, we clamp the timeout parameter at +- * 2*info->timeout. +- */ +- if (!timeout || timeout > 2 * info->timeout) +- timeout = 2 * info->timeout; +- +- expire = jiffies + timeout; +-#if DEBUG +- printk("ambauart_wait_until_sent(%d), jiff=%lu, expire=%lu...\n", +- MINOR(tty->device) - tty->driver.minor_start, jiffies, +- expire); +-#endif +- while (UART_GET_FR(info->port) & AMBA_UARTFR_BUSY) { +- set_current_state(TASK_INTERRUPTIBLE); +- schedule_timeout(char_time); +- if (signal_pending(current)) +- break; +- if (timeout && time_after(jiffies, expire)) +- break; +- status = UART_GET_FR(info->port); +- } +- set_current_state(TASK_RUNNING); +-} +- +-static void ambauart_hangup(struct tty_struct *tty) +-{ +- struct amba_info *info = tty->driver_data; +- struct amba_state *state = info->state; +- +- ambauart_flush_buffer(tty); +- if (info->flags & ASYNC_CLOSING) +- return; +- ambauart_shutdown(info); +- info->event = 0; +- state->count = 0; +- info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); +- info->tty = NULL; +- wake_up_interruptible(&info->open_wait); +-} +- +-static int block_til_ready(struct tty_struct *tty, struct file *filp, +- struct amba_info *info) +-{ +- DECLARE_WAITQUEUE(wait, current); +- struct amba_state *state = info->state; +- unsigned long flags; +- int do_clocal = 0, extra_count = 0, retval; +- +- /* +- * If the device is in the middle of being closed, then block +- * until it's done, and then try again. +- */ +- if (tty_hung_up_p(filp) || +- (info->flags & ASYNC_CLOSING)) { +- if (info->flags & ASYNC_CLOSING) +- interruptible_sleep_on(&info->close_wait); +- return (info->flags & ASYNC_HUP_NOTIFY) ? +- -EAGAIN : -ERESTARTSYS; +- } +- +- /* +- * If this is a callout device, then just make sure the normal +- * device isn't being used. +- */ +- if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { +- if (info->flags & ASYNC_NORMAL_ACTIVE) +- return -EBUSY; +- if ((info->flags & ASYNC_CALLOUT_ACTIVE) && +- (info->flags & ASYNC_SESSION_LOCKOUT) && +- (info->session != current->session)) +- return -EBUSY; +- if ((info->flags & ASYNC_CALLOUT_ACTIVE) && +- (info->flags & ASYNC_PGRP_LOCKOUT) && +- (info->pgrp != current->pgrp)) +- return -EBUSY; +- info->flags |= ASYNC_CALLOUT_ACTIVE; +- return 0; +- } +- +- /* +- * If non-blocking mode is set, or the port is not enabled, +- * then make the check up front and then exit. +- */ +- if ((filp->f_flags & O_NONBLOCK) || +- (tty->flags & (1 << TTY_IO_ERROR))) { +- if (info->flags & ASYNC_CALLOUT_ACTIVE) +- return -EBUSY; +- info->flags |= ASYNC_NORMAL_ACTIVE; +- return 0; +- } +- +- if (info->flags & ASYNC_CALLOUT_ACTIVE) { +- if (state->normal_termios.c_cflag & CLOCAL) +- do_clocal = 1; +- } else { +- 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 +- * this loop, state->count is dropped by one, so that +- * rs_close() knows when to free things. We restore it upon +- * exit, either normal or abnormal. +- */ +- retval = 0; +- add_wait_queue(&info->open_wait, &wait); +- save_flags(flags); cli(); +- if (!tty_hung_up_p(filp)) { +- extra_count = 1; +- state->count--; +- } +- restore_flags(flags); +- info->blocked_open++; +- while (1) { +- save_flags(flags); cli(); +- if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && +- (tty->termios->c_cflag & CBAUD)) { +- info->mctrl = TIOCM_DTR | TIOCM_RTS; +- info->port->set_mctrl(info->port, info->mctrl); +- } +- restore_flags(flags); +- set_current_state(TASK_INTERRUPTIBLE); +- if (tty_hung_up_p(filp) || +- !(info->flags & ASYNC_INITIALIZED)) { +- if (info->flags & ASYNC_HUP_NOTIFY) +- retval = -EAGAIN; +- else +- retval = -ERESTARTSYS; +- break; +- } +- if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && +- !(info->flags & ASYNC_CLOSING) && +- (do_clocal || (UART_GET_FR(info->port) & AMBA_UARTFR_DCD))) +- break; +- if (signal_pending(current)) { +- retval = -ERESTARTSYS; +- break; +- } +- schedule(); +- } +- set_current_state(TASK_RUNNING); +- remove_wait_queue(&info->open_wait, &wait); +- if (extra_count) +- state->count++; +- info->blocked_open--; +- if (retval) +- return retval; +- info->flags |= ASYNC_NORMAL_ACTIVE; +- return 0; +-} +- +-static struct amba_info *ambauart_get(int line) +-{ +- struct amba_info *info; +- struct amba_state *state = amba_state + line; +- +- state->count++; +- if (state->info) +- return state->info; +- info = kmalloc(sizeof(struct amba_info), GFP_KERNEL); +- if (info) { +- memset(info, 0, sizeof(struct amba_info)); +- init_waitqueue_head(&info->open_wait); +- init_waitqueue_head(&info->close_wait); +- init_waitqueue_head(&info->delta_msr_wait); +- info->flags = state->flags; +- info->state = state; +- info->port = amba_ports + line; +- tasklet_init(&info->tlet, ambauart_tasklet_action, +- (unsigned long)info); +- } +- if (state->info) { +- kfree(info); +- return state->info; +- } +- state->info = info; +- return info; +-} +- +-static int ambauart_open(struct tty_struct *tty, struct file *filp) +-{ +- struct amba_info *info; +- int retval, line = MINOR(tty->device) - tty->driver.minor_start; +- +-#if DEBUG +- printk("ambauart_open(%d) called\n", line); +-#endif +- +- // is this a line that we've got? +- MOD_INC_USE_COUNT; +- if (line >= SERIAL_AMBA_NR) { +- MOD_DEC_USE_COUNT; +- return -ENODEV; +- } +- +- info = ambauart_get(line); +- if (!info) +- return -ENOMEM; +- +- tty->driver_data = info; +- info->tty = tty; +- info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +- +- /* +- * Make sure we have the temporary buffer allocated +- */ +- if (!tmp_buf) { +- unsigned long page = get_zeroed_page(GFP_KERNEL); +- if (tmp_buf) +- free_page(page); +- else if (!page) { +- MOD_DEC_USE_COUNT; +- return -ENOMEM; +- } +- tmp_buf = (u_char *)page; +- } +- +- /* +- * If the port is in the middle of closing, bail out now. +- */ +- if (tty_hung_up_p(filp) || +- (info->flags & ASYNC_CLOSING)) { +- if (info->flags & ASYNC_CLOSING) +- interruptible_sleep_on(&info->close_wait); +- MOD_DEC_USE_COUNT; +- return -EAGAIN; +- } +- +- /* +- * Start up the serial port +- */ +- retval = ambauart_startup(info); +- if (retval) { +- MOD_DEC_USE_COUNT; +- return retval; +- } +- +- retval = block_til_ready(tty, filp, info); +- if (retval) { +- MOD_DEC_USE_COUNT; +- return retval; +- } +- +- if ((info->state->count == 1) && +- (info->flags & ASYNC_SPLIT_TERMIOS)) { +- if (tty->driver.subtype == SERIAL_TYPE_NORMAL) +- *tty->termios = info->state->normal_termios; +- else +- *tty->termios = info->state->callout_termios; +- } +-#ifdef CONFIG_SERIAL_AMBA_CONSOLE +- if (ambauart_cons.cflag && ambauart_cons.index == line) { +- tty->termios->c_cflag = ambauart_cons.cflag; +- ambauart_cons.cflag = 0; +- } +-#endif +- ambauart_change_speed(info, NULL); +- info->session = current->session; +- info->pgrp = current->pgrp; +- return 0; +-} +- +-int __init ambauart_init(void) +-{ +- int i; +- +- ambanormal_driver.magic = TTY_DRIVER_MAGIC; +- ambanormal_driver.driver_name = "serial_amba"; +- ambanormal_driver.name = SERIAL_AMBA_NAME; +- ambanormal_driver.major = SERIAL_AMBA_MAJOR; +- ambanormal_driver.minor_start = SERIAL_AMBA_MINOR; +- ambanormal_driver.num = SERIAL_AMBA_NR; +- ambanormal_driver.type = TTY_DRIVER_TYPE_SERIAL; +- ambanormal_driver.subtype = SERIAL_TYPE_NORMAL; +- ambanormal_driver.init_termios = tty_std_termios; +- ambanormal_driver.init_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; +- ambanormal_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; +- ambanormal_driver.refcount = &ambauart_refcount; +- ambanormal_driver.table = ambauart_table; +- ambanormal_driver.termios = ambauart_termios; +- ambanormal_driver.termios_locked = ambauart_termios_locked; +- +- ambanormal_driver.open = ambauart_open; +- ambanormal_driver.close = ambauart_close; +- ambanormal_driver.write = ambauart_write; +- ambanormal_driver.put_char = ambauart_put_char; +- ambanormal_driver.flush_chars = ambauart_flush_chars; +- ambanormal_driver.write_room = ambauart_write_room; +- ambanormal_driver.chars_in_buffer = ambauart_chars_in_buffer; +- ambanormal_driver.flush_buffer = ambauart_flush_buffer; +- ambanormal_driver.ioctl = ambauart_ioctl; +- ambanormal_driver.throttle = ambauart_throttle; +- ambanormal_driver.unthrottle = ambauart_unthrottle; +- ambanormal_driver.send_xchar = ambauart_send_xchar; +- ambanormal_driver.set_termios = ambauart_set_termios; +- ambanormal_driver.stop = ambauart_stop; +- ambanormal_driver.start = ambauart_start; +- ambanormal_driver.hangup = ambauart_hangup; +- ambanormal_driver.break_ctl = ambauart_break_ctl; +- ambanormal_driver.wait_until_sent = ambauart_wait_until_sent; +- ambanormal_driver.read_proc = NULL; +- +- /* +- * The callout device is just like the normal device except for +- * the major number and the subtype code. +- */ +- ambacallout_driver = ambanormal_driver; +- ambacallout_driver.name = CALLOUT_AMBA_NAME; +- ambacallout_driver.major = CALLOUT_AMBA_MAJOR; +- ambacallout_driver.subtype = SERIAL_TYPE_CALLOUT; +- ambacallout_driver.read_proc = NULL; +- ambacallout_driver.proc_entry = NULL; +- +- if (tty_register_driver(&ambanormal_driver)) +- panic("Couldn't register AMBA serial driver\n"); +- if (tty_register_driver(&ambacallout_driver)) +- panic("Couldn't register AMBA callout driver\n"); +- +- for (i = 0; i < SERIAL_AMBA_NR; i++) { +- struct amba_state *state = amba_state + i; +- state->line = i; +- state->close_delay = 5 * HZ / 10; +- state->closing_wait = 30 * HZ; +- state->callout_termios = ambacallout_driver.init_termios; +- state->normal_termios = ambanormal_driver.init_termios; +- } +- +- return 0; +-} +- +-__initcall(ambauart_init); +- +-#ifdef CONFIG_SERIAL_AMBA_CONSOLE +-/************** console driver *****************/ +- +-/* +- * This code is currently never used; console->read is never called. +- * Therefore, although we have an implementation, we don't use it. +- * FIXME: the "const char *s" should be fixed to "char *s" some day. +- * (when the definition in include/linux/console.h is also fixed) +- */ +-#ifdef used_and_not_const_char_pointer +-static int ambauart_console_read(struct console *co, const char *s, u_int count) +-{ +- struct amba_port *port = &amba_ports[co->index]; +- unsigned int status; +- char *w; +- int c; +-#if DEBUG +- printk("ambauart_console_read() called\n"); +-#endif +- +- c = 0; +- w = s; +- while (c < count) { +- status = UART_GET_FR(port); +- if (UART_RX_DATA(status)) { +- *w++ = UART_GET_CHAR(port); +- c++; +- } else { +- // nothing more to get, return +- return c; +- } +- } +- // return the count +- return c; +-} +-#endif +- +-/* +- * Print a string to the serial port trying not to disturb +- * any possible real use of the port... +- * +- * The console must be locked when we get here. +- */ +-static void ambauart_console_write(struct console *co, const char *s, u_int count) +-{ +- struct amba_port *port = &amba_ports[co->index]; +- unsigned int status, old_cr; +- int i; +- +- /* +- * First save the CR then disable the interrupts +- */ +- old_cr = UART_GET_CR(port); +- UART_PUT_CR(port, AMBA_UARTCR_UARTEN); +- +- /* +- * Now, do each character +- */ +- for (i = 0; i < count; i++) { +- do { +- status = UART_GET_FR(port); +- } while (!UART_TX_READY(status)); +- UART_PUT_CHAR(port, s[i]); +- if (s[i] == '\n') { +- do { +- status = UART_GET_FR(port); +- } while (!UART_TX_READY(status)); +- UART_PUT_CHAR(port, '\r'); +- } +- } +- +- /* +- * Finally, wait for transmitter to become empty +- * and restore the TCR +- */ +- do { +- status = UART_GET_FR(port); +- } while (status & AMBA_UARTFR_BUSY); +- UART_PUT_CR(port, old_cr); +-} +- +-static kdev_t ambauart_console_device(struct console *c) +-{ +- return MKDEV(SERIAL_AMBA_MAJOR, SERIAL_AMBA_MINOR + c->index); +-} +- +-static int __init ambauart_console_setup(struct console *co, char *options) +-{ +- struct amba_port *port; +- int baud = 38400; +- int bits = 8; +- int parity = 'n'; +- u_int cflag = CREAD | HUPCL | CLOCAL; +- u_int lcr_h, quot; +- +- if (co->index >= SERIAL_AMBA_NR) +- co->index = 0; +- +- port = &amba_ports[co->index]; +- +- if (options) { +- char *s = options; +- baud = simple_strtoul(s, NULL, 10); +- while (*s >= '0' && *s <= '9') +- s++; +- if (*s) parity = *s++; +- if (*s) bits = *s - '0'; +- } +- +- /* +- * Now construct a cflag setting. +- */ +- switch (baud) { +- case 1200: cflag |= B1200; break; +- case 2400: cflag |= B2400; break; +- case 4800: cflag |= B4800; break; +- default: cflag |= B9600; baud = 9600; break; +- case 19200: cflag |= B19200; break; +- case 38400: cflag |= B38400; break; +- case 57600: cflag |= B57600; break; +- case 115200: cflag |= B115200; break; +- } +- switch (bits) { +- case 7: cflag |= CS7; lcr_h = AMBA_UARTLCR_H_WLEN_7; break; +- default: cflag |= CS8; lcr_h = AMBA_UARTLCR_H_WLEN_8; break; +- } +- switch (parity) { +- case 'o': +- case 'O': cflag |= PARODD; lcr_h |= AMBA_UARTLCR_H_PEN; break; +- case 'e': +- case 'E': cflag |= PARENB; lcr_h |= AMBA_UARTLCR_H_PEN | +- AMBA_UARTLCR_H_EPS; break; +- } +- +- co->cflag = cflag; +- +- if (port->fifosize > 1) +- lcr_h |= AMBA_UARTLCR_H_FEN; +- +- quot = (port->uartclk / (16 * baud)) - 1; +- +- UART_PUT_LCRL(port, (quot & 0xff)); +- UART_PUT_LCRM(port, (quot >> 8)); +- UART_PUT_LCRH(port, lcr_h); +- +- /* we will enable the port as we need it */ +- UART_PUT_CR(port, 0); +- +- return 0; +-} +- +-static struct console ambauart_cons = +-{ +- name: SERIAL_AMBA_NAME, +- write: ambauart_console_write, +-#ifdef used_and_not_const_char_pointer +- read: ambauart_console_read, +-#endif +- device: ambauart_console_device, +- setup: ambauart_console_setup, +- flags: CON_PRINTBUFFER, +- index: -1, +-}; +- +-void __init ambauart_console_init(void) +-{ +- register_console(&ambauart_cons); +-} +- +-#endif /* CONFIG_SERIAL_AMBA_CONSOLE */ +- +-MODULE_LICENSE("GPL"); +-EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/char/tty_io.c linux-2.4.26-vrs1/drivers/char/tty_io.c +--- linux-2.4.26/drivers/char/tty_io.c 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/tty_io.c 2004-04-18 21:47:50.000000000 +0100 +@@ -19,7 +19,7 @@ + * Also restructured routines so that there is more of a separation + * between the high-level tty routines (tty_io.c and tty_ioctl.c) and + * the low-level tty routines (serial.c, pty.c, console.c). This +- * makes for cleaner and more compact code. -TYT, 9/17/92 ++ * makes for cleaner and more compact code. -TYT, 9/17/92 + * + * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines + * which can be dynamically activated and de-activated by the line +@@ -41,7 +41,7 @@ + * + * New TIOCLINUX variants added. + * -- mj@k332.feld.cvut.cz, 19-Nov-95 +- * ++ * + * Restrict vt switching via ioctl() + * -- grif@cs.ucr.edu, 5-Dec-95 + * +@@ -151,8 +151,7 @@ + extern void tty3215_init(void); + extern void tub3270_con_init(void); + extern void tub3270_init(void); +-extern void rs285_console_init(void); +-extern void sa1100_rs_console_init(void); ++extern void uart_console_init(void); + extern void sgi_serial_console_init(void); + extern void sn_sal_serial_console_init(void); + extern void sci_console_init(void); +@@ -164,6 +163,7 @@ + extern void txx9_serial_console_init(void); + extern void sb1250_serial_console_init(void); + extern void arc_console_init(void); ++extern void rs285_console_init(void); + extern int hvc_console_init(void); + + #ifndef MIN +@@ -201,7 +201,7 @@ + else + sprintf(buf, name, + idx + tty->driver.name_base); +- ++ + return buf; + } + +@@ -239,7 +239,7 @@ + #ifdef CHECK_TTY_COUNT + struct list_head *p; + int count = 0; +- ++ + file_list_lock(); + for(p = tty->tty_files.next; p != &tty->tty_files; p = p->next) { + if(list_entry(p, struct file, f_list)->private_data == tty) +@@ -255,7 +255,7 @@ + "!= #fd's(%d) in %s\n", + kdevname(tty->device), tty->count, count, routine); + return count; +- } ++ } + #endif + return 0; + } +@@ -264,14 +264,14 @@ + { + if (disc < N_TTY || disc >= NR_LDISCS) + return -EINVAL; +- ++ + if (new_ldisc) { + ldiscs[disc] = *new_ldisc; + ldiscs[disc].flags |= LDISC_FLAG_DEFINED; + ldiscs[disc].num = disc; + } else + memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc)); +- ++ + return 0; + } + +@@ -301,7 +301,7 @@ + o_ldisc = tty->ldisc; + + tty_wait_until_sent(tty, 0); +- ++ + /* Shutdown the current discipline. */ + if (tty->ldisc.close) + (tty->ldisc.close)(tty); +@@ -339,7 +339,7 @@ + { + int major, minor; + struct tty_driver *p; +- ++ + minor = MINOR(device); + major = MAJOR(device); + +@@ -456,7 +456,7 @@ + redirect = NULL; + } + spin_unlock(&redirect_lock); +- ++ + check_tty_count(tty, "do_tty_hangup"); + file_list_lock(); + for (l = tty->tty_files.next; l != &tty->tty_files; l = l->next) { +@@ -473,7 +473,7 @@ + filp->f_op = &hung_up_tty_fops; + } + file_list_unlock(); +- ++ + /* FIXME! What are the locking issues here? This may me overdoing things.. */ + { + unsigned long flags; +@@ -510,7 +510,7 @@ + "error %d\n", -i); + } + } +- ++ + read_lock(&tasklist_lock); + for_each_task(p) { + if ((tty->session > 0) && (p->session == tty->session) && +@@ -550,7 +550,7 @@ + { + #ifdef TTY_DEBUG_HANGUP + char buf[64]; +- ++ + printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf)); + #endif + schedule_task(&tty->tq_hangup); +@@ -650,7 +650,7 @@ + wake_up_interruptible(&tty->write_wait); + } + +-static ssize_t tty_read(struct file * file, char * buf, size_t count, ++static ssize_t tty_read(struct file * file, char * buf, size_t count, + loff_t *ppos) + { + int i; +@@ -707,7 +707,7 @@ + size_t count) + { + ssize_t ret = 0, written = 0; +- ++ + if (file->f_flags & O_NONBLOCK) { + if (down_trylock(&tty->atomic_write)) + return -EAGAIN; +@@ -835,7 +835,7 @@ + struct tty_struct *tty, *o_tty; + struct termios *tp, **tp_loc, *o_tp, **o_tp_loc; + struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; +- struct tty_driver *driver; ++ struct tty_driver *driver; + int retval=0; + int idx; + +@@ -845,7 +845,7 @@ + + idx = MINOR(device) - driver->minor_start; + +- /* ++ /* + * Check whether we need to acquire the tty semaphore to avoid + * race conditions. For now, play it safe. + */ +@@ -859,7 +859,7 @@ + * First time open is complex, especially for PTY devices. + * This code guarantees that either everything succeeds and the + * TTY is ready for operation, or else the table slots are vacated +- * and the allocated memory released. (Except that the termios ++ * and the allocated memory released. (Except that the termios + * and locked termios may be retained.) + */ + +@@ -938,13 +938,13 @@ + o_tty->link = tty; + } + +- /* ++ /* + * All structures have been allocated, so now we install them. +- * Failures after this point use release_mem to clean up, so ++ * Failures after this point use release_mem to clean up, so + * there's no need to null out the local pointers. + */ + driver->table[idx] = tty; +- ++ + if (!*tp_loc) + *tp_loc = tp; + if (!*ltp_loc) +@@ -954,7 +954,7 @@ + (*driver->refcount)++; + tty->count++; + +- /* ++ /* + * Structures all installed ... call the ldisc open routines. + * If we fail here just call release_mem to clean up. No need + * to decrement the use counts, as release_mem doesn't care. +@@ -988,7 +988,7 @@ + if (driver->type == TTY_DRIVER_TYPE_PTY && + driver->subtype == PTY_TYPE_MASTER) { + /* +- * special case for PTY masters: only one open permitted, ++ * special case for PTY masters: only one open permitted, + * and the slave side open count is incremented as well. + */ + if (tty->count) { +@@ -1002,7 +1002,7 @@ + + success: + *ret_tty = tty; +- ++ + /* All paths come through here to release the semaphore */ + end_init: + up_tty_sem(idx); +@@ -1080,7 +1080,7 @@ + int pty_master, tty_closing, o_tty_closing, do_sleep; + int idx; + char buf[64]; +- ++ + tty = (struct tty_struct *)filp->private_data; + if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "release_dev")) + return; +@@ -1138,7 +1138,7 @@ + idx, kdevname(tty->device)); + return; + } +- if (o_tty->termios_locked != ++ if (o_tty->termios_locked != + tty->driver.other->termios_locked[idx]) { + printk(KERN_DEBUG "release_dev: other->termios_locked[" + "%d] not o_termios_locked for (%s)\n", +@@ -1204,11 +1204,11 @@ + printk(KERN_WARNING "release_dev: %s: read/write wait queue " + "active!\n", tty_name(tty, buf)); + schedule(); +- } ++ } + + /* +- * The closing flags are now consistent with the open counts on +- * both sides, and we've completed the last operation that could ++ * The closing flags are now consistent with the open counts on ++ * both sides, and we've completed the last operation that could + * block, so it's safe to proceed with closing. + */ + if (pty_master) { +@@ -1266,7 +1266,7 @@ + /* check whether both sides are closing ... */ + if (!tty_closing || (o_tty && !o_tty_closing)) + return; +- ++ + #ifdef TTY_DEBUG_HANGUP + printk(KERN_DEBUG "freeing tty structure..."); + #endif +@@ -1284,14 +1284,14 @@ + (o_tty->ldisc.close)(o_tty); + o_tty->ldisc = ldiscs[N_TTY]; + } +- ++ + /* +- * Make sure that the tty's task queue isn't activated. ++ * Make sure that the tty's task queue isn't activated. + */ + run_task_queue(&tq_timer); + flush_scheduled_tasks(); + +- /* ++ /* + * The release_mem function takes care of the details of clearing + * the slots and preserving the termios structure. + */ +@@ -1482,7 +1482,7 @@ + tty = (struct tty_struct *)filp->private_data; + if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "tty_fasync")) + return 0; +- ++ + retval = fasync_helper(fd, filp, on, &tty->fasync); + if (retval <= 0) + return retval; +@@ -1697,7 +1697,7 @@ + + static int tty_generic_brk(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) + { +- if (cmd == TCSBRK && arg) ++ if (cmd == TCSBRK && arg) + { + /* tcdrain case */ + int retval = tty_check_change(tty); +@@ -1718,7 +1718,7 @@ + { + struct tty_struct *tty, *real_tty; + int retval; +- ++ + tty = (struct tty_struct *)file->private_data; + if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl")) + return -EINVAL; +@@ -1738,7 +1738,7 @@ + if (tty->driver.ioctl) + return tty->driver.ioctl(tty, file, cmd, arg); + return -EINVAL; +- ++ + /* These two ioctl's always return success; even if */ + /* the driver doesn't support them. */ + case TCSBRK: +@@ -1761,7 +1761,7 @@ + case TIOCSBRK: + case TIOCCBRK: + case TCSBRK: +- case TCSBRKP: ++ case TCSBRKP: + retval = tty_check_change(tty); + if (retval) + return retval; +@@ -1824,7 +1824,7 @@ + case TIOCSBRK: /* Turn break on, unconditionally */ + tty->driver.break_ctl(tty, -1); + return 0; +- ++ + case TIOCCBRK: /* Turn break off, unconditionally */ + tty->driver.break_ctl(tty, 0); + return 0; +@@ -1837,7 +1837,7 @@ + if (!arg) + return send_break(tty, HZ/4); + return 0; +- case TCSBRKP: /* support for POSIX tcsendbreak() */ ++ case TCSBRKP: /* support for POSIX tcsendbreak() */ + return send_break(tty, arg ? arg*(HZ/10) : HZ/4); + } + if (tty->driver.ioctl) { +@@ -1859,7 +1859,7 @@ + * prevent trojan horses by killing all processes associated with this + * tty when the user hits the "Secure Attention Key". Required for + * super-paranoid applications --- see the Orange Book for more details. +- * ++ * + * This code could be nicer; ideally it should send a HUP, wait a few + * seconds, then send a INT, and then a KILL signal. But you then + * have to coordinate with the init process, since all processes associated +@@ -1883,7 +1883,7 @@ + int session; + int i; + struct file *filp; +- ++ + if (!tty) + return; + session = tty->session; +@@ -1968,7 +1968,7 @@ + count = tty->flip.count; + tty->flip.count = 0; + restore_flags(flags); +- ++ + tty->ldisc.receive_buf(tty, cp, fp, count); + } + +@@ -2000,7 +2000,7 @@ + i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~CBAUDEX; +- if (i < 1 || i+15 >= n_baud_table) ++ if (i < 1 || i+15 >= n_baud_table) + tty->termios->c_cflag &= ~CBAUDEX; + else + i += 15; +@@ -2013,7 +2013,7 @@ + } + return(tty->alt_speed); + } +- ++ + return baud_table[i]; + } + +@@ -2079,7 +2079,7 @@ + mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + break; + } +- if ( (minor < driver->minor_start) || ++ if ( (minor < driver->minor_start) || + (minor >= driver->minor_start + driver->num) ) { + printk(KERN_ERR "Attempt to register invalid minor number " + "with devfs (%d:%d).\n", (int)driver->major,(int)minor); +@@ -2132,12 +2132,12 @@ + + if (!driver->put_char) + driver->put_char = tty_default_put_char; +- ++ + driver->prev = 0; + driver->next = tty_drivers; + if (tty_drivers) tty_drivers->prev = driver; + tty_drivers = driver; +- ++ + if ( !(driver->flags & TTY_DRIVER_NO_DEVFS) ) { + for(i = 0; i < driver->num; i++) + tty_register_devfs(driver, 0, driver->minor_start + i); +@@ -2156,7 +2156,7 @@ + int i, found = 0; + struct termios *tp; + const char *othername = NULL; +- ++ + if (*driver->refcount) + return -EBUSY; + +@@ -2166,7 +2166,7 @@ + else if (p->major == driver->major) + othername = p->name; + } +- ++ + if (!found) + return -ENOENT; + +@@ -2181,7 +2181,7 @@ + driver->prev->next = driver->next; + else + tty_drivers = driver->next; +- ++ + if (driver->next) + driver->next->prev = driver->prev; + +@@ -2221,7 +2221,7 @@ + (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); + + /* +- * Set up the standard termios. Individual tty drivers may ++ * Set up the standard termios. Individual tty drivers may + * deviate from this; this is used as a template. + */ + memset(&tty_std_termios, 0, sizeof(struct termios)); +@@ -2233,11 +2233,11 @@ + ECHOCTL | ECHOKE | IEXTEN; + + /* +- * set up the console device so that later boot sequences can ++ * set up the console device so that later boot sequences can + * inform about problems etc.. + */ + #ifdef CONFIG_EARLY_PRINTK +- disable_early_printk(); ++ disable_early_printk(); + #endif + #ifdef CONFIG_HVC_CONSOLE + hvc_console_init(); +@@ -2288,18 +2288,12 @@ + #ifdef CONFIG_STDIO_CONSOLE + stdio_console_init(); + #endif +-#ifdef CONFIG_SERIAL_21285_CONSOLE +- rs285_console_init(); +-#endif +-#ifdef CONFIG_SERIAL_SA1100_CONSOLE +- sa1100_rs_console_init(); ++#ifdef CONFIG_SERIAL_CORE_CONSOLE ++ uart_console_init(); + #endif + #ifdef CONFIG_ARC_CONSOLE + arc_console_init(); + #endif +-#ifdef CONFIG_SERIAL_AMBA_CONSOLE +- ambauart_console_init(); +-#endif + #ifdef CONFIG_SERIAL_TX3912_CONSOLE + tx3912_console_init(); + #endif +@@ -2315,6 +2309,9 @@ + #ifdef CONFIG_IP22_SERIAL + sgi_serial_console_init(); + #endif ++#ifdef CONFIG_SERIAL_21285_CONSOLE ++ rs285_console_init(); ++#endif + } + + static struct tty_driver dev_tty_driver, dev_syscons_driver; +@@ -2347,7 +2344,7 @@ + dev_tty_driver.num = 1; + dev_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM; + dev_tty_driver.subtype = SYSTEM_TYPE_TTY; +- ++ + if (tty_register_driver(&dev_tty_driver)) + panic("Couldn't register /dev/tty driver\n"); + +@@ -2363,7 +2360,7 @@ + panic("Couldn't register /dev/console driver\n"); + + /* console calls tty_register_driver() before kmalloc() works. +- * Thus, we can't devfs_register() then. Do so now, instead. ++ * Thus, we can't devfs_register() then. Do so now, instead. + */ + #ifdef CONFIG_VT + con_init_devfs(); +@@ -2381,7 +2378,7 @@ + if (tty_register_driver(&dev_ptmx_driver)) + panic("Couldn't register /dev/ptmx driver\n"); + #endif +- ++ + #ifdef CONFIG_VT + dev_console_driver = dev_tty_driver; + dev_console_driver.driver_name = "/dev/vc/0"; +@@ -2441,10 +2438,10 @@ + pty_init(); + #ifdef CONFIG_MOXA_SMARTIO + mxser_init(); +-#endif ++#endif + #ifdef CONFIG_MOXA_INTELLIO + moxa_init(); +-#endif ++#endif + #ifdef CONFIG_VT + vcs_init(); + #endif +diff -urN linux-2.4.26/drivers/char/wdt285.c linux-2.4.26-vrs1/drivers/char/wdt285.c +--- linux-2.4.26/drivers/char/wdt285.c 2003-06-13 15:51:33.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/char/wdt285.c 2004-01-14 21:32:25.000000000 +0000 +@@ -151,7 +151,7 @@ + if (get_user(new_margin, (int *)arg)) + return -EFAULT; + /* Arbitrary, can't find the card's limits */ +- if ((new_marg < 0) || (new_margin > 60)) ++ if ((new_margin < 0) || (new_margin > 60)) + return -EINVAL; + soft_margin = new_margin; + watchdog_ping(); +diff -urN linux-2.4.26/drivers/char/wdt977.c linux-2.4.26-vrs1/drivers/char/wdt977.c +--- linux-2.4.26/drivers/char/wdt977.c 2002-11-28 23:53:12.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/char/wdt977.c 2004-01-14 21:32:25.000000000 +0000 +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + #define WATCHDOG_MINOR 130 + +diff -urN linux-2.4.26/drivers/cpufreq/Kconfig linux-2.4.26-vrs1/drivers/cpufreq/Kconfig +--- linux-2.4.26/drivers/cpufreq/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/cpufreq/Kconfig 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,38 @@ ++config CPU_FREQ_PROC_INTF ++ tristate "/proc/cpufreq interface (deprecated)" ++ depends on CPU_FREQ && PROC_FS ++ help ++ This enables the /proc/cpufreq interface for controlling ++ CPUFreq. Please note that it is recommended to use the sysfs ++ interface instead (which is built automatically). ++ ++ For details, take a look at linux/Documentation/cpufreq. ++ ++ If in doubt, say N. ++ ++config CPU_FREQ_GOV_USERSPACE ++ tristate "'userspace' governor for userspace frequency scaling" ++ depends on CPU_FREQ ++ help ++ Enable this cpufreq governor when you either want to set the ++ CPU frequency manually or when an userspace programm shall ++ be able to set the CPU dynamically, like on LART ++ ( http://www.lart.tudelft.nl/ ) ++ ++ For details, take a look at linux/Documentation/cpufreq. ++ ++ If in doubt, say Y. ++ ++config CPU_FREQ_24_API ++ bool "/proc/sys/cpu/ interface (2.4. / OLD)" ++ depends on CPU_FREQ && SYSCTL && CPU_FREQ_GOV_USERSPACE ++ help ++ This enables the /proc/sys/cpu/ sysctl interface for controlling ++ the CPUFreq,"userspace" governor. This is the same interface ++ as known from the.4.-kernel patches for CPUFreq, and offers ++ the same functionality as long as "userspace" is the ++ selected governor for the specified CPU. ++ ++ For details, take a look at linux/Documentation/cpufreq. ++ ++ If in doubt, say N. +diff -urN linux-2.4.26/drivers/cpufreq/Makefile linux-2.4.26-vrs1/drivers/cpufreq/Makefile +--- linux-2.4.26/drivers/cpufreq/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/cpufreq/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,4 @@ ++#CPUfreq governors and cross-arch helpers ++obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o ++obj-$(CONFIG_CPU_FREQ_PROC_INTF) += proc_intf.o ++obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += userspace.o +diff -urN linux-2.4.26/drivers/cpufreq/cpufreq.c linux-2.4.26-vrs1/drivers/cpufreq/cpufreq.c +--- linux-2.4.26/drivers/cpufreq/cpufreq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/cpufreq/cpufreq.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,720 @@ ++/* ++ * linux/kernel/cpufreq.c ++ * ++ * Copyright (C) 2001 Russell King ++ * (C) 2002 - 2003 Dominik Brodowski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++/** ++ * The "cpufreq driver" - the arch- or hardware-dependend low ++ * level driver of CPUFreq support, and its spinlock. This lock ++ * also protects the cpufreq_cpu_data array. ++ */ ++static struct cpufreq_driver *cpufreq_driver; ++static struct cpufreq_policy *cpufreq_cpu_data[NR_CPUS]; ++static spinlock_t cpufreq_driver_lock = SPIN_LOCK_UNLOCKED; ++ ++/* internal prototype */ ++static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event); ++ ++ ++/** ++ * Two notifier lists: the "policy" list is involved in the ++ * validation process for a new CPU frequency policy; the ++ * "transition" list for kernel code that needs to handle ++ * changes to devices when the CPU clock speed changes. ++ * The mutex locks both lists. ++ */ ++static struct notifier_block *cpufreq_policy_notifier_list; ++static struct notifier_block *cpufreq_transition_notifier_list; ++static DECLARE_RWSEM (cpufreq_notifier_rwsem); ++ ++ ++static LIST_HEAD(cpufreq_governor_list); ++static DECLARE_MUTEX (cpufreq_governor_sem); ++ ++/* ++ * backport info: ++ * we don't have a kobj we can use for ref-counting, so use a ++ * "unsigned int policy->use_count" and an "unload_sem" [idea from ++ * Pat Mochel's struct driver unload_sem] for proper reference counting. ++ */ ++ ++static struct cpufreq_policy * cpufreq_cpu_get(unsigned int cpu) ++{ ++ struct cpufreq_policy *data; ++ unsigned long flags; ++ ++ if (cpu >= NR_CPUS) ++ goto err_out; ++ ++ /* get the cpufreq driver */ ++ spin_lock_irqsave(&cpufreq_driver_lock, flags); ++ ++ if (!cpufreq_driver) ++ goto err_out_unlock; ++ ++ /* get the CPU */ ++ data = cpufreq_cpu_data[cpu]; ++ ++ if (!data) ++ goto err_out_unlock; ++ ++ if (!data->use_count) ++ goto err_out_unlock; ++ ++ data->use_count += 1; ++ ++ spin_unlock_irqrestore(&cpufreq_driver_lock, flags); ++ ++ return data; ++ ++ err_out_unlock: ++ spin_unlock_irqrestore(&cpufreq_driver_lock, flags); ++ err_out: ++ return NULL; ++} ++ ++static void cpufreq_cpu_put(struct cpufreq_policy *data) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cpufreq_driver_lock, flags); ++ data->use_count -= 1; ++ if (!data->use_count) { ++ spin_unlock_irqrestore(&cpufreq_driver_lock, flags); ++ up(&data->unload_sem); ++ return; ++ } ++ spin_unlock_irqrestore(&cpufreq_driver_lock, flags); ++} ++ ++/********************************************************************* ++ * SYSFS INTERFACE * ++ *********************************************************************/ ++ ++/** ++ * cpufreq_parse_governor - parse a governor string ++ */ ++int cpufreq_parse_governor (char *str_governor, unsigned int *policy, ++ struct cpufreq_governor **governor) ++{ ++ if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) { ++ *policy = CPUFREQ_POLICY_PERFORMANCE; ++ return 0; ++ } else if (!strnicmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) { ++ *policy = CPUFREQ_POLICY_POWERSAVE; ++ return 0; ++ } else { ++ struct cpufreq_governor *t; ++ down(&cpufreq_governor_sem); ++ if (!cpufreq_driver || !cpufreq_driver->target) ++ goto out; ++ list_for_each_entry(t, &cpufreq_governor_list, governor_list) { ++ if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) { ++ *governor = t; ++ *policy = CPUFREQ_POLICY_GOVERNOR; ++ up(&cpufreq_governor_sem); ++ return 0; ++ } ++ } ++ out: ++ up(&cpufreq_governor_sem); ++ } ++ return -EINVAL; ++} ++EXPORT_SYMBOL_GPL(cpufreq_parse_governor); ++ ++ ++/* backport info: ++ * all the sysfs stuff is missing -- of course ++ */ ++ ++/** ++ * cpufreq_add_dev - add a CPU device ++ * ++ * Adds the cpufreq interface for a CPU device. ++ */ ++static int cpufreq_add_dev (unsigned int cpu) ++{ ++ int ret = 0; ++ struct cpufreq_policy new_policy; ++ struct cpufreq_policy *policy; ++ unsigned long flags; ++ ++ policy = kmalloc(sizeof(struct cpufreq_policy), GFP_KERNEL); ++ if (!policy) ++ return -ENOMEM; ++ memset(policy, 0, sizeof(struct cpufreq_policy)); ++ ++ policy->cpu = cpu; ++ policy->use_count = 1; ++ init_MUTEX_LOCKED(&policy->lock); ++ init_MUTEX_LOCKED(&policy->unload_sem); ++ ++ /* call driver. From then on the cpufreq must be able ++ * to accept all calls to ->verify and ->setpolicy for this CPU ++ */ ++ ret = cpufreq_driver->init(policy); ++ if (ret) ++ goto err_out; ++ ++ memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); ++ ++ spin_lock_irqsave(&cpufreq_driver_lock, flags); ++ cpufreq_cpu_data[cpu] = policy; ++ spin_unlock_irqrestore(&cpufreq_driver_lock, flags); ++ ++ up(&policy->lock); ++ ++ /* set default policy */ ++ ret = cpufreq_set_policy(&new_policy); ++ if (ret) ++ goto err_out_unregister; ++ ++ return 0; ++ ++ ++ err_out_unregister: ++ spin_lock_irqsave(&cpufreq_driver_lock, flags); ++ cpufreq_cpu_data[cpu] = NULL; ++ spin_unlock_irqrestore(&cpufreq_driver_lock, flags); ++ ++ err_out: ++ kfree(policy); ++ return ret; ++} ++ ++ ++/** ++ * cpufreq_remove_dev - remove a CPU device ++ * ++ * Removes the cpufreq interface for a CPU device. ++ */ ++static int cpufreq_remove_dev (unsigned int cpu) ++{ ++ unsigned long flags; ++ struct cpufreq_policy *data; ++ ++ spin_lock_irqsave(&cpufreq_driver_lock, flags); ++ data = cpufreq_cpu_data[cpu]; ++ if (!data) { ++ spin_unlock_irqrestore(&cpufreq_driver_lock, flags); ++ return -EINVAL; ++ } ++ cpufreq_cpu_data[cpu] = NULL; ++ ++ data->use_count -= 1; ++ if (!data->use_count) { ++ spin_unlock_irqrestore(&cpufreq_driver_lock, flags); ++ up(&data->unload_sem); ++ } else { ++ spin_unlock_irqrestore(&cpufreq_driver_lock, flags); ++ /* this will sleep until data->use_count gets to zero */ ++ down(&data->unload_sem); ++ up(&data->unload_sem); ++ } ++ ++ if (cpufreq_driver->target) ++ __cpufreq_governor(data, CPUFREQ_GOV_STOP); ++ ++ if (cpufreq_driver->exit) ++ cpufreq_driver->exit(data); ++ ++ kfree(data); ++ ++ return 0; ++} ++ ++ ++/********************************************************************* ++ * NOTIFIER LISTS INTERFACE * ++ *********************************************************************/ ++ ++/** ++ * cpufreq_register_notifier - register a driver with cpufreq ++ * @nb: notifier function to register ++ * @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER ++ * ++ * Add a driver to one of two lists: either a list of drivers that ++ * are notified about clock rate changes (once before and once after ++ * the transition), or a list of drivers that are notified about ++ * changes in cpufreq policy. ++ * ++ * This function may sleep, and has the same return conditions as ++ * notifier_chain_register. ++ */ ++int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) ++{ ++ int ret; ++ ++ down_write(&cpufreq_notifier_rwsem); ++ switch (list) { ++ case CPUFREQ_TRANSITION_NOTIFIER: ++ ret = notifier_chain_register(&cpufreq_transition_notifier_list, nb); ++ break; ++ case CPUFREQ_POLICY_NOTIFIER: ++ ret = notifier_chain_register(&cpufreq_policy_notifier_list, nb); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ up_write(&cpufreq_notifier_rwsem); ++ ++ return ret; ++} ++EXPORT_SYMBOL(cpufreq_register_notifier); ++ ++ ++/** ++ * cpufreq_unregister_notifier - unregister a driver with cpufreq ++ * @nb: notifier block to be unregistered ++ * @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER ++ * ++ * Remove a driver from the CPU frequency notifier list. ++ * ++ * This function may sleep, and has the same return conditions as ++ * notifier_chain_unregister. ++ */ ++int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) ++{ ++ int ret; ++ ++ down_write(&cpufreq_notifier_rwsem); ++ switch (list) { ++ case CPUFREQ_TRANSITION_NOTIFIER: ++ ret = notifier_chain_unregister(&cpufreq_transition_notifier_list, nb); ++ break; ++ case CPUFREQ_POLICY_NOTIFIER: ++ ret = notifier_chain_unregister(&cpufreq_policy_notifier_list, nb); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ up_write(&cpufreq_notifier_rwsem); ++ ++ return ret; ++} ++EXPORT_SYMBOL(cpufreq_unregister_notifier); ++ ++ ++/********************************************************************* ++ * GOVERNORS * ++ *********************************************************************/ ++ ++ ++int __cpufreq_driver_target(struct cpufreq_policy *policy, ++ unsigned int target_freq, ++ unsigned int relation) ++{ ++ return cpufreq_driver->target(policy, target_freq, relation); ++} ++EXPORT_SYMBOL_GPL(__cpufreq_driver_target); ++ ++ ++int cpufreq_driver_target(struct cpufreq_policy *policy, ++ unsigned int target_freq, ++ unsigned int relation) ++{ ++ unsigned int ret; ++ ++ policy = cpufreq_cpu_get(policy->cpu); ++ if (!policy) ++ return -EINVAL; ++ ++ down(&policy->lock); ++ ++ ret = __cpufreq_driver_target(policy, target_freq, relation); ++ ++ up(&policy->lock); ++ ++ cpufreq_cpu_put(policy); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(cpufreq_driver_target); ++ ++ ++static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event) ++{ ++ int ret = 0; ++ ++ switch (policy->policy) { ++ case CPUFREQ_POLICY_POWERSAVE: ++ if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) { ++ ret = __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); ++ } ++ break; ++ case CPUFREQ_POLICY_PERFORMANCE: ++ if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) { ++ ret = __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); ++ } ++ break; ++ case CPUFREQ_POLICY_GOVERNOR: ++ ret = policy->governor->governor(policy, event); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++ ++int cpufreq_governor(unsigned int cpu, unsigned int event) ++{ ++ int ret = 0; ++ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); ++ ++ if (!policy) ++ return -EINVAL; ++ ++ down(&policy->lock); ++ ret = __cpufreq_governor(policy, event); ++ up(&policy->lock); ++ ++ cpufreq_cpu_put(policy); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(cpufreq_governor); ++ ++ ++int cpufreq_register_governor(struct cpufreq_governor *governor) ++{ ++ struct cpufreq_governor *t; ++ ++ if (!governor) ++ return -EINVAL; ++ ++ if (!strnicmp(governor->name,"powersave",CPUFREQ_NAME_LEN)) ++ return -EBUSY; ++ if (!strnicmp(governor->name,"performance",CPUFREQ_NAME_LEN)) ++ return -EBUSY; ++ ++ down(&cpufreq_governor_sem); ++ ++ list_for_each_entry(t, &cpufreq_governor_list, governor_list) { ++ if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) { ++ up(&cpufreq_governor_sem); ++ return -EBUSY; ++ } ++ } ++ list_add(&governor->governor_list, &cpufreq_governor_list); ++ ++ up(&cpufreq_governor_sem); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(cpufreq_register_governor); ++ ++ ++void cpufreq_unregister_governor(struct cpufreq_governor *governor) ++{ ++ /* backport info: ++ * As the module usage count isn't assured in 2.4., check for removal ++ * of running cpufreq governor ++ */ ++ unsigned int i; ++ ++ if (!governor) ++ return; ++ ++ down(&cpufreq_governor_sem); ++ ++ for (i=0; ilock); ++ ++ if (policy->policy != CPUFREQ_POLICY_GOVERNOR) ++ goto unlock_done; ++ if (policy->governor != governor) ++ goto unlock_done; ++ ++ /* stop old one, start performance [always present] */ ++ __cpufreq_governor(policy, CPUFREQ_GOV_STOP); ++ policy->policy = CPUFREQ_POLICY_PERFORMANCE; ++ __cpufreq_governor(policy, CPUFREQ_GOV_START); ++ ++ unlock_done: ++ up(&policy->lock); ++ done: ++ cpufreq_cpu_put(policy); ++ } ++ list_del(&governor->governor_list); ++ up(&cpufreq_governor_sem); ++ return; ++} ++EXPORT_SYMBOL_GPL(cpufreq_unregister_governor); ++ ++ ++ ++/********************************************************************* ++ * POLICY INTERFACE * ++ *********************************************************************/ ++ ++/** ++ * cpufreq_get_policy - get the current cpufreq_policy ++ * @policy: struct cpufreq_policy into which the current cpufreq_policy is written ++ * ++ * Reads the current cpufreq policy. ++ */ ++int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu) ++{ ++ struct cpufreq_policy *cpu_policy; ++ if (!policy) ++ return -EINVAL; ++ ++ cpu_policy = cpufreq_cpu_get(cpu); ++ if (!cpu_policy) ++ return -EINVAL; ++ ++ down(&cpu_policy->lock); ++ memcpy(policy, cpu_policy, sizeof(struct cpufreq_policy)); ++ up(&cpu_policy->lock); ++ ++ cpufreq_cpu_put(cpu_policy); ++ ++ return 0; ++} ++EXPORT_SYMBOL(cpufreq_get_policy); ++ ++ ++/** ++ * cpufreq_set_policy - set a new CPUFreq policy ++ * @policy: policy to be set. ++ * ++ * Sets a new CPU frequency and voltage scaling policy. ++ */ ++int cpufreq_set_policy(struct cpufreq_policy *policy) ++{ ++ int ret = 0; ++ struct cpufreq_policy *data; ++ ++ if (!policy) ++ return -EINVAL; ++ ++ data = cpufreq_cpu_get(policy->cpu); ++ if (!data) ++ return -EINVAL; ++ ++ /* lock this CPU */ ++ down(&data->lock); ++ ++ memcpy(&policy->cpuinfo, ++ &data->cpuinfo, ++ sizeof(struct cpufreq_cpuinfo)); ++ ++ /* verify the cpu speed can be set within this limit */ ++ ret = cpufreq_driver->verify(policy); ++ if (ret) ++ goto error_out; ++ ++ down_read(&cpufreq_notifier_rwsem); ++ ++ /* adjust if necessary - all reasons */ ++ notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST, ++ policy); ++ ++ /* adjust if necessary - hardware incompatibility*/ ++ notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_INCOMPATIBLE, ++ policy); ++ ++ /* verify the cpu speed can be set within this limit, ++ which might be different to the first one */ ++ ret = cpufreq_driver->verify(policy); ++ if (ret) { ++ up_read(&cpufreq_notifier_rwsem); ++ goto error_out; ++ } ++ ++ /* notification of the new policy */ ++ notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY, ++ policy); ++ ++ up_read(&cpufreq_notifier_rwsem); ++ ++ data->min = policy->min; ++ data->max = policy->max; ++ ++ if (cpufreq_driver->setpolicy) { ++ data->policy = policy->policy; ++ ret = cpufreq_driver->setpolicy(policy); ++ } else { ++ if ((policy->policy != data->policy) || ++ ((policy->policy == CPUFREQ_POLICY_GOVERNOR) && (policy->governor != data->governor))) { ++ /* save old, working values */ ++ unsigned int old_pol = data->policy; ++ struct cpufreq_governor *old_gov = data->governor; ++ ++ /* end old governor */ ++ __cpufreq_governor(data, CPUFREQ_GOV_STOP); ++ ++ /* start new governor */ ++ data->policy = policy->policy; ++ data->governor = policy->governor; ++ if (__cpufreq_governor(data, CPUFREQ_GOV_START)) { ++ /* new governor failed, so re-start old one */ ++ data->policy = old_pol; ++ data->governor = old_gov; ++ __cpufreq_governor(data, CPUFREQ_GOV_START); ++ } ++ /* might be a policy change, too, so fall through */ ++ } ++ __cpufreq_governor(data, CPUFREQ_GOV_LIMITS); ++ } ++ ++ error_out: ++ up(&data->lock); ++ cpufreq_cpu_put(data); ++ ++ return ret; ++} ++EXPORT_SYMBOL(cpufreq_set_policy); ++ ++ ++ ++/********************************************************************* ++ * EXTERNALLY AFFECTING FREQUENCY CHANGES * ++ *********************************************************************/ ++ ++/** ++ * adjust_jiffies - adjust the system "loops_per_jiffy" ++ * ++ * This function alters the system "loops_per_jiffy" for the clock ++ * speed change. Note that loops_per_jiffy cannot be updated on SMP ++ * systems as each CPU might be scaled differently. So, use the arch ++ * per-CPU loops_per_jiffy value wherever possible. ++ */ ++#ifndef CONFIG_SMP ++static unsigned long l_p_j_ref = 0; ++static unsigned int l_p_j_ref_freq = 0; ++ ++static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) ++{ ++ if (!l_p_j_ref_freq) { ++ l_p_j_ref = loops_per_jiffy; ++ l_p_j_ref_freq = ci->old; ++ } ++ if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) || ++ (val == CPUFREQ_POSTCHANGE && ci->old > ci->new)) ++ loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new); ++} ++#else ++#define adjust_jiffies(x...) do {} while (0) ++#endif ++ ++ ++/** ++ * cpufreq_notify_transition - call notifier chain and adjust_jiffies on frequency transition ++ * ++ * This function calls the transition notifiers and the "adjust_jiffies" function. It is called ++ * twice on all CPU frequency changes that have external effects. ++ */ ++void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) ++{ ++ down_read(&cpufreq_notifier_rwsem); ++ switch (state) { ++ case CPUFREQ_PRECHANGE: ++ notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs); ++ adjust_jiffies(CPUFREQ_PRECHANGE, freqs); ++ break; ++ case CPUFREQ_POSTCHANGE: ++ adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); ++ notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs); ++ cpufreq_cpu_data[freqs->cpu]->cur = freqs->new; ++ break; ++ } ++ up_read(&cpufreq_notifier_rwsem); ++} ++EXPORT_SYMBOL_GPL(cpufreq_notify_transition); ++ ++ ++ ++/********************************************************************* ++ * REGISTER / UNREGISTER CPUFREQ DRIVER * ++ *********************************************************************/ ++ ++/** ++ * cpufreq_register_driver - register a CPU Frequency driver ++ * @driver_data: A struct cpufreq_driver containing the values# ++ * submitted by the CPU Frequency driver. ++ * ++ * Registers a CPU Frequency driver to this core code. This code ++ * returns zero on success, -EBUSY when another driver got here first ++ * (and isn't unregistered in the meantime). ++ * ++ */ ++int cpufreq_register_driver(struct cpufreq_driver *driver_data) ++{ ++ unsigned long flags; ++ unsigned int i; ++ ++ if (!driver_data || !driver_data->verify || !driver_data->init || ++ ((!driver_data->setpolicy) && (!driver_data->target))) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&cpufreq_driver_lock, flags); ++ if (cpufreq_driver) { ++ spin_unlock_irqrestore(&cpufreq_driver_lock, flags); ++ return -EBUSY; ++ } ++ cpufreq_driver = driver_data; ++ spin_unlock_irqrestore(&cpufreq_driver_lock, flags); ++ ++ for (i=0; i ++#include ++#include ++#include ++ ++/********************************************************************* ++ * FREQUENCY TABLE HELPERS * ++ *********************************************************************/ ++ ++int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, ++ struct cpufreq_frequency_table *table) ++{ ++ unsigned int min_freq = ~0; ++ unsigned int max_freq = 0; ++ unsigned int i = 0; ++ ++ for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { ++ unsigned int freq = table[i].frequency; ++ if (freq == CPUFREQ_ENTRY_INVALID) ++ continue; ++ if (freq < min_freq) ++ min_freq = freq; ++ if (freq > max_freq) ++ max_freq = freq; ++ } ++ ++ policy->min = policy->cpuinfo.min_freq = min_freq; ++ policy->max = policy->cpuinfo.max_freq = max_freq; ++ ++ if (policy->min == ~0) ++ return -EINVAL; ++ else ++ return 0; ++} ++EXPORT_SYMBOL_GPL(cpufreq_frequency_table_cpuinfo); ++ ++ ++int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, ++ struct cpufreq_frequency_table *table) ++{ ++ unsigned int next_larger = ~0; ++ unsigned int i = 0; ++ unsigned int count = 0; ++ ++ if (!cpu_online(policy->cpu)) ++ return -EINVAL; ++ ++ cpufreq_verify_within_limits(policy, ++ policy->cpuinfo.min_freq, ++ policy->cpuinfo.max_freq); ++ ++ for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { ++ unsigned int freq = table[i].frequency; ++ if (freq == CPUFREQ_ENTRY_INVALID) ++ continue; ++ if ((freq >= policy->min) && (freq <= policy->max)) ++ count++; ++ else if ((next_larger > freq) && (freq > policy->max)) ++ next_larger = freq; ++ } ++ ++ if (!count) ++ policy->max = next_larger; ++ ++ cpufreq_verify_within_limits(policy, ++ policy->cpuinfo.min_freq, ++ policy->cpuinfo.max_freq); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify); ++ ++ ++int cpufreq_frequency_table_target(struct cpufreq_policy *policy, ++ struct cpufreq_frequency_table *table, ++ unsigned int target_freq, ++ unsigned int relation, ++ unsigned int *index) ++{ ++ struct cpufreq_frequency_table optimal = { .index = ~0, }; ++ struct cpufreq_frequency_table suboptimal = { .index = ~0, }; ++ unsigned int i; ++ ++ switch (relation) { ++ case CPUFREQ_RELATION_H: ++ optimal.frequency = 0; ++ suboptimal.frequency = ~0; ++ break; ++ case CPUFREQ_RELATION_L: ++ optimal.frequency = ~0; ++ suboptimal.frequency = 0; ++ break; ++ } ++ ++ if (!cpu_online(policy->cpu)) ++ return -EINVAL; ++ ++ for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { ++ unsigned int freq = table[i].frequency; ++ if (freq == CPUFREQ_ENTRY_INVALID) ++ continue; ++ if ((freq < policy->min) || (freq > policy->max)) ++ continue; ++ switch(relation) { ++ case CPUFREQ_RELATION_H: ++ if (freq <= target_freq) { ++ if (freq >= optimal.frequency) { ++ optimal.frequency = freq; ++ optimal.index = i; ++ } ++ } else { ++ if (freq <= suboptimal.frequency) { ++ suboptimal.frequency = freq; ++ suboptimal.index = i; ++ } ++ } ++ break; ++ case CPUFREQ_RELATION_L: ++ if (freq >= target_freq) { ++ if (freq <= optimal.frequency) { ++ optimal.frequency = freq; ++ optimal.index = i; ++ } ++ } else { ++ if (freq >= suboptimal.frequency) { ++ suboptimal.frequency = freq; ++ suboptimal.index = i; ++ } ++ } ++ break; ++ } ++ } ++ if (optimal.index > i) { ++ if (suboptimal.index > i) ++ return -EINVAL; ++ *index = suboptimal.index; ++ } else ++ *index = optimal.index; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target); ++ ++static struct cpufreq_frequency_table *show_table[NR_CPUS]; ++/** ++ * show_scaling_governor - show the current policy for the specified CPU ++ */ ++static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf) ++{ ++ unsigned int i = 0; ++ unsigned int cpu = policy->cpu; ++ ssize_t count = 0; ++ struct cpufreq_frequency_table *table; ++ ++ if (!show_table[cpu]) ++ return -ENODEV; ++ ++ table = show_table[cpu]; ++ ++ for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { ++ if (table[i].frequency == CPUFREQ_ENTRY_INVALID) ++ continue; ++ count += sprintf(&buf[count], "%d ", table[i].frequency); ++ } ++ count += sprintf(&buf[count], "\n"); ++ ++ return count; ++ ++} ++ ++struct freq_attr cpufreq_freq_attr_scaling_available_freqs = { ++ .attr = { .name = "scaling_available_frequencies", .mode = 0444 }, ++ .show = show_available_freqs, ++}; ++EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs); ++ ++/* ++ * if you use these, you must assure that the frequency table is valid ++ * all the time between get_attr and put_attr! ++ */ ++void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table, ++ unsigned int cpu) ++{ ++ show_table[cpu] = table; ++} ++EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr); ++ ++void cpufreq_frequency_table_put_attr(unsigned int cpu) ++{ ++ show_table[cpu] = NULL; ++} ++EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr); ++ ++ ++MODULE_AUTHOR ("Dominik Brodowski "); ++MODULE_DESCRIPTION ("CPUfreq frequency table helpers"); ++MODULE_LICENSE ("GPL"); +diff -urN linux-2.4.26/drivers/cpufreq/proc_intf.c linux-2.4.26-vrs1/drivers/cpufreq/proc_intf.c +--- linux-2.4.26/drivers/cpufreq/proc_intf.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/cpufreq/proc_intf.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,244 @@ ++/* ++ * linux/drivers/cpufreq/proc_intf.c ++ * ++ * Copyright (C) 2002 - 2003 Dominik Brodowski ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/** ++ * cpufreq_parse_policy - parse a policy string ++ * @input_string: the string to parse. ++ * @policy: the policy written inside input_string ++ * ++ * This function parses a "policy string" - something the user echo'es into ++ * /proc/cpufreq or gives as boot parameter - into a struct cpufreq_policy. ++ * If there are invalid/missing entries, they are replaced with current ++ * cpufreq policy. ++ */ ++static int cpufreq_parse_policy(char input_string[42], struct cpufreq_policy *policy) ++{ ++ unsigned int min = 0; ++ unsigned int max = 0; ++ unsigned int cpu = 0; ++ char str_governor[16]; ++ struct cpufreq_policy current_policy; ++ unsigned int result = -EFAULT; ++ ++ if (!policy) ++ return -EINVAL; ++ ++ policy->min = 0; ++ policy->max = 0; ++ policy->policy = 0; ++ policy->cpu = CPUFREQ_ALL_CPUS; ++ ++ if (sscanf(input_string, "%d:%d:%d:%15s", &cpu, &min, &max, str_governor) == 4) ++ { ++ policy->min = min; ++ policy->max = max; ++ policy->cpu = cpu; ++ result = 0; ++ goto scan_policy; ++ } ++ if (sscanf(input_string, "%d%%%d%%%d%%%15s", &cpu, &min, &max, str_governor) == 4) ++ { ++ if (!cpufreq_get_policy(¤t_policy, cpu)) { ++ policy->min = (min * current_policy.cpuinfo.max_freq) / 100; ++ policy->max = (max * current_policy.cpuinfo.max_freq) / 100; ++ policy->cpu = cpu; ++ result = 0; ++ goto scan_policy; ++ } ++ } ++ ++ if (sscanf(input_string, "%d:%d:%15s", &min, &max, str_governor) == 3) ++ { ++ policy->min = min; ++ policy->max = max; ++ result = 0; ++ goto scan_policy; ++ } ++ ++ if (sscanf(input_string, "%d%%%d%%%15s", &min, &max, str_governor) == 3) ++ { ++ if (!cpufreq_get_policy(¤t_policy, cpu)) { ++ policy->min = (min * current_policy.cpuinfo.max_freq) / 100; ++ policy->max = (max * current_policy.cpuinfo.max_freq) / 100; ++ result = 0; ++ goto scan_policy; ++ } ++ } ++ ++ return -EINVAL; ++ ++scan_policy: ++ result = cpufreq_parse_governor(str_governor, &policy->policy, &policy->governor); ++ ++ return result; ++} ++ ++/** ++ * cpufreq_proc_read - read /proc/cpufreq ++ * ++ * This function prints out the current cpufreq policy. ++ */ ++static int cpufreq_proc_read ( ++ char *page, ++ char **start, ++ off_t off, ++ int count, ++ int *eof, ++ void *data) ++{ ++ char *p = page; ++ int len = 0; ++ struct cpufreq_policy policy; ++ unsigned int min_pctg = 0; ++ unsigned int max_pctg = 0; ++ unsigned int i = 0; ++ ++ if (off != 0) ++ goto end; ++ ++ p += sprintf(p, " minimum CPU frequency - maximum CPU frequency - policy\n"); ++ for (i=0;iname); ++ break; ++ default: ++ p += sprintf(p, "INVALID\n"); ++ break; ++ } ++ } ++end: ++ len = (p - page); ++ if (len <= off+count) ++ *eof = 1; ++ *start = page + off; ++ len -= off; ++ if (len>count) ++ len = count; ++ if (len<0) ++ len = 0; ++ ++ return len; ++} ++ ++ ++/** ++ * cpufreq_proc_write - handles writing into /proc/cpufreq ++ * ++ * This function calls the parsing script and then sets the policy ++ * accordingly. ++ */ ++static int cpufreq_proc_write ( ++ struct file *file, ++ const char *buffer, ++ unsigned long count, ++ void *data) ++{ ++ int result = 0; ++ char proc_string[42] = {'\0'}; ++ struct cpufreq_policy policy; ++ unsigned int i = 0; ++ ++ ++ if ((count > sizeof(proc_string) - 1)) ++ return -EINVAL; ++ ++ if (copy_from_user(proc_string, buffer, count)) ++ return -EFAULT; ++ ++ proc_string[count] = '\0'; ++ ++ result = cpufreq_parse_policy(proc_string, &policy); ++ if (result) ++ return -EFAULT; ++ ++ if (policy.cpu == CPUFREQ_ALL_CPUS) ++ { ++ for (i=0; iread_proc = cpufreq_proc_read; ++ entry->write_proc = cpufreq_proc_write; ++ } ++ ++ return 0; ++} ++ ++ ++/** ++ * cpufreq_proc_exit - removes "cpufreq" from the /proc root directory. ++ * ++ * This function removes "cpufreq" from the /proc root directory. ++ */ ++static void __exit cpufreq_proc_exit (void) ++{ ++ remove_proc_entry("cpufreq", &proc_root); ++ return; ++} ++ ++MODULE_AUTHOR ("Dominik Brodowski "); ++MODULE_DESCRIPTION ("CPUfreq /proc/cpufreq interface"); ++MODULE_LICENSE ("GPL"); ++ ++module_init(cpufreq_proc_init); ++module_exit(cpufreq_proc_exit); +diff -urN linux-2.4.26/drivers/cpufreq/userspace.c linux-2.4.26-vrs1/drivers/cpufreq/userspace.c +--- linux-2.4.26/drivers/cpufreq/userspace.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/cpufreq/userspace.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,591 @@ ++/* ++ * drivers/cpufreq/userspace.c ++ * ++ * Copyright (C) 2001 Russell King ++ * (C) 2002 - 2003 Dominik Brodowski ++ * ++ * $Id: userspace.c,v 1.4 2003/03/07 10:30:20 db Exp $ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define CTL_CPU_VARS_SPEED_MAX(cpunr) { \ ++ .ctl_name = CPU_NR_FREQ_MAX, \ ++ .data = &cpu_max_freq[cpunr], \ ++ .procname = "speed-max", \ ++ .maxlen = sizeof(cpu_max_freq[cpunr]),\ ++ .mode = 0444, \ ++ .proc_handler = proc_dointvec, } ++ ++#define CTL_CPU_VARS_SPEED_MIN(cpunr) { \ ++ .ctl_name = CPU_NR_FREQ_MIN, \ ++ .data = &cpu_min_freq[cpunr], \ ++ .procname = "speed-min", \ ++ .maxlen = sizeof(cpu_min_freq[cpunr]),\ ++ .mode = 0444, \ ++ .proc_handler = proc_dointvec, } ++ ++#define CTL_CPU_VARS_SPEED(cpunr) { \ ++ .ctl_name = CPU_NR_FREQ, \ ++ .procname = "speed", \ ++ .mode = 0644, \ ++ .proc_handler = cpufreq_procctl, \ ++ .strategy = cpufreq_sysctl, \ ++ .extra1 = (void*) (cpunr), } ++ ++#define CTL_TABLE_CPU_VARS(cpunr) static ctl_table ctl_cpu_vars_##cpunr[] = {\ ++ CTL_CPU_VARS_SPEED_MAX(cpunr), \ ++ CTL_CPU_VARS_SPEED_MIN(cpunr), \ ++ CTL_CPU_VARS_SPEED(cpunr), \ ++ { .ctl_name = 0, }, } ++ ++/* the ctl_table entry for each CPU */ ++#define CPU_ENUM(s) { \ ++ .ctl_name = (CPU_NR + s), \ ++ .procname = #s, \ ++ .mode = 0555, \ ++ .child = ctl_cpu_vars_##s } ++ ++/** ++ * A few values needed by the userspace governor ++ */ ++static unsigned int cpu_max_freq[NR_CPUS]; ++static unsigned int cpu_min_freq[NR_CPUS]; ++static unsigned int cpu_cur_freq[NR_CPUS]; ++static unsigned int cpu_is_managed[NR_CPUS]; ++static struct cpufreq_policy current_policy[NR_CPUS]; ++ ++static DECLARE_MUTEX (userspace_sem); ++ ++ ++/* keep track of frequency transitions */ ++static int ++userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val, ++ void *data) ++{ ++ struct cpufreq_freqs *freq = data; ++ ++ cpu_cur_freq[freq->cpu] = freq->new; ++ ++ return 0; ++} ++ ++static struct notifier_block userspace_cpufreq_notifier_block = { ++ .notifier_call = userspace_cpufreq_notifier ++}; ++ ++ ++/** ++ * cpufreq_set - set the CPU frequency ++ * @freq: target frequency in kHz ++ * @cpu: CPU for which the frequency is to be set ++ * ++ * Sets the CPU frequency to freq. ++ */ ++int cpufreq_set(unsigned int freq, unsigned int cpu) ++{ ++ int ret = -EINVAL; ++ ++ down(&userspace_sem); ++ if (!cpu_is_managed[cpu]) ++ goto err; ++ ++ if (freq < cpu_min_freq[cpu]) ++ freq = cpu_min_freq[cpu]; ++ if (freq > cpu_max_freq[cpu]) ++ freq = cpu_max_freq[cpu]; ++ ++ ret = cpufreq_driver_target(¤t_policy[cpu], freq, ++ CPUFREQ_RELATION_L); ++ ++ err: ++ up(&userspace_sem); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(cpufreq_set); ++ ++ ++/** ++ * cpufreq_setmax - set the CPU to the maximum frequency ++ * @cpu - affected cpu; ++ * ++ * Sets the CPU frequency to the maximum frequency supported by ++ * this CPU. ++ */ ++int cpufreq_setmax(unsigned int cpu) ++{ ++ if (!cpu_is_managed[cpu] || !cpu_online(cpu)) ++ return -EINVAL; ++ return cpufreq_set(cpu_max_freq[cpu], cpu); ++} ++EXPORT_SYMBOL_GPL(cpufreq_setmax); ++ ++ ++/** ++ * cpufreq_get - get the current CPU frequency (in kHz) ++ * @cpu: CPU number ++ * ++ * Get the CPU current (static) CPU frequency ++ */ ++unsigned int cpufreq_get(unsigned int cpu) ++{ ++ return cpu_cur_freq[cpu]; ++} ++EXPORT_SYMBOL(cpufreq_get); ++ ++ ++#ifdef CONFIG_CPU_FREQ_24_API ++ ++ ++/*********************** cpufreq_sysctl interface ********************/ ++static int ++cpufreq_procctl(ctl_table *ctl, int write, struct file *filp, ++ void *buffer, size_t *lenp) ++{ ++ char buf[16], *p; ++ int cpu = (int) ctl->extra1; ++ int len, left = *lenp; ++ ++ if (!left || (filp->f_pos && !write) || !cpu_online(cpu)) { ++ *lenp = 0; ++ return 0; ++ } ++ ++ if (write) { ++ unsigned int freq; ++ ++ len = left; ++ if (left > sizeof(buf)) ++ left = sizeof(buf); ++ if (copy_from_user(buf, buffer, left)) ++ return -EFAULT; ++ buf[sizeof(buf) - 1] = '\0'; ++ ++ freq = simple_strtoul(buf, &p, 0); ++ cpufreq_set(freq, cpu); ++ } else { ++ len = sprintf(buf, "%d\n", cpufreq_get(cpu)); ++ if (len > left) ++ len = left; ++ if (copy_to_user(buffer, buf, len)) ++ return -EFAULT; ++ } ++ ++ *lenp = len; ++ filp->f_pos += len; ++ return 0; ++} ++ ++static int ++cpufreq_sysctl(ctl_table *table, int *name, int nlen, ++ void *oldval, size_t *oldlenp, ++ void *newval, size_t newlen, void **context) ++{ ++ int cpu = (int) table->extra1; ++ ++ if (!cpu_online(cpu)) ++ return -EINVAL; ++ ++ if (oldval && oldlenp) { ++ size_t oldlen; ++ ++ if (get_user(oldlen, oldlenp)) ++ return -EFAULT; ++ ++ if (oldlen != sizeof(unsigned int)) ++ return -EINVAL; ++ ++ if (put_user(cpufreq_get(cpu), (unsigned int *)oldval) || ++ put_user(sizeof(unsigned int), oldlenp)) ++ return -EFAULT; ++ } ++ if (newval && newlen) { ++ unsigned int freq; ++ ++ if (newlen != sizeof(unsigned int)) ++ return -EINVAL; ++ ++ if (get_user(freq, (unsigned int *)newval)) ++ return -EFAULT; ++ ++ cpufreq_set(freq, cpu); ++ } ++ return 1; ++} ++ ++/* ctl_table ctl_cpu_vars_{0,1,...,(NR_CPUS-1)} */ ++/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */ ++ CTL_TABLE_CPU_VARS(0); ++#if NR_CPUS > 1 ++ CTL_TABLE_CPU_VARS(1); ++#endif ++#if NR_CPUS > 2 ++ CTL_TABLE_CPU_VARS(2); ++#endif ++#if NR_CPUS > 3 ++ CTL_TABLE_CPU_VARS(3); ++#endif ++#if NR_CPUS > 4 ++ CTL_TABLE_CPU_VARS(4); ++#endif ++#if NR_CPUS > 5 ++ CTL_TABLE_CPU_VARS(5); ++#endif ++#if NR_CPUS > 6 ++ CTL_TABLE_CPU_VARS(6); ++#endif ++#if NR_CPUS > 7 ++ CTL_TABLE_CPU_VARS(7); ++#endif ++#if NR_CPUS > 8 ++ CTL_TABLE_CPU_VARS(8); ++#endif ++#if NR_CPUS > 9 ++ CTL_TABLE_CPU_VARS(9); ++#endif ++#if NR_CPUS > 10 ++ CTL_TABLE_CPU_VARS(10); ++#endif ++#if NR_CPUS > 11 ++ CTL_TABLE_CPU_VARS(11); ++#endif ++#if NR_CPUS > 12 ++ CTL_TABLE_CPU_VARS(12); ++#endif ++#if NR_CPUS > 13 ++ CTL_TABLE_CPU_VARS(13); ++#endif ++#if NR_CPUS > 14 ++ CTL_TABLE_CPU_VARS(14); ++#endif ++#if NR_CPUS > 15 ++ CTL_TABLE_CPU_VARS(15); ++#endif ++#if NR_CPUS > 16 ++ CTL_TABLE_CPU_VARS(16); ++#endif ++#if NR_CPUS > 17 ++ CTL_TABLE_CPU_VARS(17); ++#endif ++#if NR_CPUS > 18 ++ CTL_TABLE_CPU_VARS(18); ++#endif ++#if NR_CPUS > 19 ++ CTL_TABLE_CPU_VARS(19); ++#endif ++#if NR_CPUS > 20 ++ CTL_TABLE_CPU_VARS(20); ++#endif ++#if NR_CPUS > 21 ++ CTL_TABLE_CPU_VARS(21); ++#endif ++#if NR_CPUS > 22 ++ CTL_TABLE_CPU_VARS(22); ++#endif ++#if NR_CPUS > 23 ++ CTL_TABLE_CPU_VARS(23); ++#endif ++#if NR_CPUS > 24 ++ CTL_TABLE_CPU_VARS(24); ++#endif ++#if NR_CPUS > 25 ++ CTL_TABLE_CPU_VARS(25); ++#endif ++#if NR_CPUS > 26 ++ CTL_TABLE_CPU_VARS(26); ++#endif ++#if NR_CPUS > 27 ++ CTL_TABLE_CPU_VARS(27); ++#endif ++#if NR_CPUS > 28 ++ CTL_TABLE_CPU_VARS(28); ++#endif ++#if NR_CPUS > 29 ++ CTL_TABLE_CPU_VARS(29); ++#endif ++#if NR_CPUS > 30 ++ CTL_TABLE_CPU_VARS(30); ++#endif ++#if NR_CPUS > 31 ++ CTL_TABLE_CPU_VARS(31); ++#endif ++#if NR_CPUS > 32 ++#error please extend CPU enumeration ++#endif ++ ++/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */ ++static ctl_table ctl_cpu_table[NR_CPUS + 1] = { ++ CPU_ENUM(0), ++#if NR_CPUS > 1 ++ CPU_ENUM(1), ++#endif ++#if NR_CPUS > 2 ++ CPU_ENUM(2), ++#endif ++#if NR_CPUS > 3 ++ CPU_ENUM(3), ++#endif ++#if NR_CPUS > 4 ++ CPU_ENUM(4), ++#endif ++#if NR_CPUS > 5 ++ CPU_ENUM(5), ++#endif ++#if NR_CPUS > 6 ++ CPU_ENUM(6), ++#endif ++#if NR_CPUS > 7 ++ CPU_ENUM(7), ++#endif ++#if NR_CPUS > 8 ++ CPU_ENUM(8), ++#endif ++#if NR_CPUS > 9 ++ CPU_ENUM(9), ++#endif ++#if NR_CPUS > 10 ++ CPU_ENUM(10), ++#endif ++#if NR_CPUS > 11 ++ CPU_ENUM(11), ++#endif ++#if NR_CPUS > 12 ++ CPU_ENUM(12), ++#endif ++#if NR_CPUS > 13 ++ CPU_ENUM(13), ++#endif ++#if NR_CPUS > 14 ++ CPU_ENUM(14), ++#endif ++#if NR_CPUS > 15 ++ CPU_ENUM(15), ++#endif ++#if NR_CPUS > 16 ++ CPU_ENUM(16), ++#endif ++#if NR_CPUS > 17 ++ CPU_ENUM(17), ++#endif ++#if NR_CPUS > 18 ++ CPU_ENUM(18), ++#endif ++#if NR_CPUS > 19 ++ CPU_ENUM(19), ++#endif ++#if NR_CPUS > 20 ++ CPU_ENUM(20), ++#endif ++#if NR_CPUS > 21 ++ CPU_ENUM(21), ++#endif ++#if NR_CPUS > 22 ++ CPU_ENUM(22), ++#endif ++#if NR_CPUS > 23 ++ CPU_ENUM(23), ++#endif ++#if NR_CPUS > 24 ++ CPU_ENUM(24), ++#endif ++#if NR_CPUS > 25 ++ CPU_ENUM(25), ++#endif ++#if NR_CPUS > 26 ++ CPU_ENUM(26), ++#endif ++#if NR_CPUS > 27 ++ CPU_ENUM(27), ++#endif ++#if NR_CPUS > 28 ++ CPU_ENUM(28), ++#endif ++#if NR_CPUS > 29 ++ CPU_ENUM(29), ++#endif ++#if NR_CPUS > 30 ++ CPU_ENUM(30), ++#endif ++#if NR_CPUS > 31 ++ CPU_ENUM(31), ++#endif ++#if NR_CPUS > 32 ++#error please extend CPU enumeration ++#endif ++ { ++ .ctl_name = 0, ++ } ++}; ++ ++static ctl_table ctl_cpu[2] = { ++ { ++ .ctl_name = CTL_CPU, ++ .procname = "cpu", ++ .mode = 0555, ++ .child = ctl_cpu_table, ++ }, ++ { ++ .ctl_name = 0, ++ } ++}; ++ ++struct ctl_table_header *cpufreq_sysctl_table; ++ ++static inline void cpufreq_sysctl_init(void) ++{ ++ cpufreq_sysctl_table = register_sysctl_table(ctl_cpu, 0); ++} ++ ++static inline void cpufreq_sysctl_exit(void) ++{ ++ unregister_sysctl_table(cpufreq_sysctl_table); ++} ++ ++#else ++#define cpufreq_sysctl_init() do {} while(0) ++#define cpufreq_sysctl_exit() do {} while(0) ++#endif /* CONFIG_CPU_FREQ_24API */ ++ ++ ++/************************** sysfs interface ************************/ ++static ssize_t show_speed (struct cpufreq_policy *policy, char *buf) ++{ ++ return sprintf (buf, "%u\n", cpu_cur_freq[policy->cpu]); ++} ++ ++static ssize_t ++store_speed (struct cpufreq_policy *policy, const char *buf, size_t count) ++{ ++ unsigned int freq = 0; ++ unsigned int ret; ++ ++ ret = sscanf (buf, "%u", &freq); ++ if (ret != 1) ++ return -EINVAL; ++ ++ cpufreq_set(freq, policy->cpu); ++ ++ return count; ++} ++ ++static struct freq_attr freq_attr_scaling_setspeed = { ++ .attr = { .name = "scaling_setspeed", .mode = 0644 }, ++ .show = show_speed, ++ .store = store_speed, ++}; ++ ++static int cpufreq_governor_userspace(struct cpufreq_policy *policy, ++ unsigned int event) ++{ ++ unsigned int cpu = policy->cpu; ++ switch (event) { ++ case CPUFREQ_GOV_START: ++ if ((!cpu_online(cpu)) || (!try_module_get(THIS_MODULE)) || ++ !policy->cur) ++ return -EINVAL; ++ down(&userspace_sem); ++ cpu_is_managed[cpu] = 1; ++ cpu_min_freq[cpu] = policy->min; ++ cpu_max_freq[cpu] = policy->max; ++ cpu_cur_freq[cpu] = policy->cur; ++ sysfs_create_file (&policy->kobj, &freq_attr_scaling_setspeed.attr); ++ memcpy (¤t_policy[cpu], policy, sizeof(struct cpufreq_policy)); ++ up(&userspace_sem); ++ break; ++ case CPUFREQ_GOV_STOP: ++ down(&userspace_sem); ++ cpu_is_managed[cpu] = 0; ++ cpu_min_freq[cpu] = 0; ++ cpu_max_freq[cpu] = 0; ++ sysfs_remove_file (&policy->kobj, &freq_attr_scaling_setspeed.attr); ++ up(&userspace_sem); ++ module_put(THIS_MODULE); ++ break; ++ case CPUFREQ_GOV_LIMITS: ++ down(&userspace_sem); ++ cpu_min_freq[cpu] = policy->min; ++ cpu_max_freq[cpu] = policy->max; ++ if (policy->max < cpu_cur_freq[cpu]) ++ cpufreq_driver_target(¤t_policy[cpu], policy->max, ++ CPUFREQ_RELATION_H); ++ else if (policy->min > cpu_cur_freq[cpu]) ++ cpufreq_driver_target(¤t_policy[cpu], policy->min, ++ CPUFREQ_RELATION_L); ++ memcpy (¤t_policy[cpu], policy, sizeof(struct cpufreq_policy)); ++ up(&userspace_sem); ++ break; ++ } ++ return 0; ++} ++ ++/* on ARM SA1100 we need to rely on the values of cpufreq_get() - because ++ * of this, cpu_cur_freq[] needs to be set early. ++ */ ++#if defined(CONFIG_ARM) && defined(CONFIG_ARCH_SA1100) ++extern unsigned int sa11x0_getspeed(void); ++ ++static void cpufreq_sa11x0_compat(void) ++{ ++ cpu_cur_freq[0] = sa11x0_getspeed(); ++} ++#else ++#define cpufreq_sa11x0_compat() do {} while(0) ++#endif ++ ++ ++static struct cpufreq_governor cpufreq_gov_userspace = { ++ .name = "userspace", ++ .governor = cpufreq_governor_userspace, ++ .owner = THIS_MODULE, ++}; ++EXPORT_SYMBOL(cpufreq_gov_userspace); ++ ++static int already_init = 0; ++ ++int cpufreq_gov_userspace_init(void) ++{ ++ if (!already_init) { ++ down(&userspace_sem); ++ cpufreq_sa11x0_compat(); ++ cpufreq_sysctl_init(); ++ cpufreq_register_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); ++ already_init = 1; ++ up(&userspace_sem); ++ } ++ return cpufreq_register_governor(&cpufreq_gov_userspace); ++} ++EXPORT_SYMBOL(cpufreq_gov_userspace_init); ++ ++ ++static void __exit cpufreq_gov_userspace_exit(void) ++{ ++ cpufreq_unregister_governor(&cpufreq_gov_userspace); ++ cpufreq_unregister_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); ++ cpufreq_sysctl_exit(); ++} ++ ++ ++MODULE_AUTHOR ("Dominik Brodowski , Russell King "); ++MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'"); ++MODULE_LICENSE ("GPL"); ++ ++module_init(cpufreq_gov_userspace_init); ++module_exit(cpufreq_gov_userspace_exit); +diff -urN linux-2.4.26/drivers/i2c/Config.in linux-2.4.26-vrs1/drivers/i2c/Config.in +--- linux-2.4.26/drivers/i2c/Config.in 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/i2c/Config.in 2004-04-18 21:47:50.000000000 +0100 +@@ -17,6 +17,15 @@ + int ' GPIO pin used for SCL' CONFIG_SCx200_I2C_SCL 12 + int ' GPIO pin used for SDA' CONFIG_SCx200_I2C_SDA 13 + fi ++ ++ dep_tristate ' Guide GPIO adapter' CONFIG_I2C_GUIDE $CONFIG_I2C_ALGOBIT ++ if [ "$CONFIG_ARCH_OMAHA" = "y" ]; then ++ dep_tristate ' Omaha I2C interface' CONFIG_I2C_OMAHA $CONFIG_I2C ++ fi ++ if [ "$CONFIG_ARCH_SA1100" = "y" ]; then ++ dep_tristate ' Frodo I2C adapter' CONFIG_I2C_FRODO $CONFIG_I2C_ALGOBIT ++ dep_tristate ' SA1100 I2C GPIO adapter' CONFIG_I2C_BIT_SA1100_GPIO $CONFIG_I2C_ALGOBIT ++ fi + fi + + dep_tristate 'NatSemi SCx200 ACCESS.bus' CONFIG_SCx200_ACB $CONFIG_I2C +@@ -49,6 +58,10 @@ + dep_tristate 'Keywest I2C interface in Apple Core99 machines' CONFIG_I2C_KEYWEST $CONFIG_I2C + fi + ++ if [ "$CONFIG_ARCH_AT91RM9200" = "y" ] ; then ++ dep_tristate 'Atmel AT91RM9200 I2C Two-Wire interface (TWI)' CONFIG_I2C_AT91 $CONFIG_I2C ++ fi ++ + if [ "$CONFIG_SIBYTE_SB1xxx_SOC" = "y" ]; then + dep_tristate 'SiByte SMBus interface' CONFIG_I2C_ALGO_SIBYTE $CONFIG_I2C + dep_tristate ' MAX1617 Temperature Sensor' CONFIG_I2C_MAX1617 $CONFIG_I2C_ALGO_SIBYTE +diff -urN linux-2.4.26/drivers/i2c/Makefile linux-2.4.26-vrs1/drivers/i2c/Makefile +--- linux-2.4.26/drivers/i2c/Makefile 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/i2c/Makefile 2004-02-23 22:59:17.000000000 +0000 +@@ -8,12 +8,21 @@ + i2c-algo-ite.o i2c-algo-sibyte.o i2c-algo-sgi.o \ + i2c-proc.o + ++# Init order: core, chardev, bit adapters, pcf adapters ++ + obj-$(CONFIG_I2C) += i2c-core.o + obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o ++ ++# Bit adapters + obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o + obj-$(CONFIG_I2C_PHILIPSPAR) += i2c-philips-par.o + obj-$(CONFIG_I2C_ELV) += i2c-elv.o + obj-$(CONFIG_I2C_VELLEMAN) += i2c-velleman.o ++obj-$(CONFIG_I2C_GUIDE) += i2c-guide.o ++obj-$(CONFIG_I2C_FRODO) += i2c-frodo.o ++obj-$(CONFIG_I2C_OMAHA) += i2c-omaha.o ++ ++# PCF adapters + obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o + obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o + obj-$(CONFIG_ITE_I2C_ALGO) += i2c-algo-ite.o +@@ -30,4 +39,3 @@ + # This is needed for automatic patch generation: sensors code ends here + + include $(TOPDIR)/Rules.make +- +diff -urN linux-2.4.26/drivers/i2c/i2c-algo-bit.c linux-2.4.26-vrs1/drivers/i2c/i2c-algo-bit.c +--- linux-2.4.26/drivers/i2c/i2c-algo-bit.c 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/i2c/i2c-algo-bit.c 2004-02-23 13:36:30.000000000 +0000 +@@ -169,7 +169,14 @@ + * 1 if the device acknowledged + * 0 if the device did not ack + * -ETIMEDOUT if an error occurred (while raising the scl line) +- */ ++ ++ * tsong@iders.ca: an instruction to disable any timeconsuming interrupt ++ here except the heart beat of timer2 should be added before setsda(adap, sb). ++ because when interrupt occurs during ++ scl is set high, the interrupt service routine is served and may take long, ++ after the interrupt returns, sda could be sampled by the device(slave) ++ more than once, and this cause error problem. ++*/ + static int i2c_outb(struct i2c_adapter *i2c_adap, char c) + { + int i; +@@ -582,9 +589,7 @@ + printk("\n"); + } + +-#ifdef MODULE + MOD_INC_USE_COUNT; +-#endif + i2c_add_adapter(adap); + + return 0; +@@ -600,15 +605,13 @@ + + DEB2(printk("i2c-algo-bit.o: adapter unregistered: %s\n",adap->name)); + +-#ifdef MODULE + MOD_DEC_USE_COUNT; +-#endif + return 0; + } + +-int __init i2c_algo_bit_init (void) ++static int __init i2c_algo_bit_init (void) + { +- printk(KERN_INFO "i2c-algo-bit.o: i2c bit algorithm module\n"); ++ printk(KERN_DEBUG "i2c-algo-bit.o: i2c bit algorithm module\n"); + return 0; + } + +@@ -617,7 +620,6 @@ + EXPORT_SYMBOL(i2c_bit_add_bus); + EXPORT_SYMBOL(i2c_bit_del_bus); + +-#ifdef MODULE + MODULE_AUTHOR("Simon G. Vogl "); + MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm"); + MODULE_LICENSE("GPL"); +@@ -631,12 +633,4 @@ + MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 bit-protocol"); + +-int init_module(void) +-{ +- return i2c_algo_bit_init(); +-} +- +-void cleanup_module(void) +-{ +-} +-#endif ++module_init(i2c_algo_bit_init); +diff -urN linux-2.4.26/drivers/i2c/i2c-algo-pcf.c linux-2.4.26-vrs1/drivers/i2c/i2c-algo-pcf.c +--- linux-2.4.26/drivers/i2c/i2c-algo-pcf.c 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/i2c/i2c-algo-pcf.c 2004-02-23 13:36:30.000000000 +0000 +@@ -475,9 +475,7 @@ + return i; + } + +-#ifdef MODULE + MOD_INC_USE_COUNT; +-#endif + + i2c_add_adapter(adap); + +@@ -515,13 +513,11 @@ + return res; + DEB2(printk("i2c-algo-pcf.o: adapter unregistered: %s\n",adap->name)); + +-#ifdef MODULE + MOD_DEC_USE_COUNT; +-#endif + return 0; + } + +-int __init i2c_algo_pcf_init (void) ++static int __init i2c_algo_pcf_init (void) + { + printk("i2c-algo-pcf.o: i2c pcf8584 algorithm module\n"); + return 0; +@@ -531,7 +527,6 @@ + EXPORT_SYMBOL(i2c_pcf_add_bus); + EXPORT_SYMBOL(i2c_pcf_del_bus); + +-#ifdef MODULE + MODULE_AUTHOR("Hans Berglund "); + MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); + MODULE_LICENSE("GPL"); +@@ -543,13 +538,4 @@ + MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol"); + +- +-int init_module(void) +-{ +- return i2c_algo_pcf_init(); +-} +- +-void cleanup_module(void) +-{ +-} +-#endif ++module_init(i2c_algo_pcf_init); +diff -urN linux-2.4.26/drivers/i2c/i2c-core.c linux-2.4.26-vrs1/drivers/i2c/i2c-core.c +--- linux-2.4.26/drivers/i2c/i2c-core.c 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/i2c/i2c-core.c 2004-02-27 22:54:22.000000000 +0000 +@@ -41,7 +41,7 @@ + + /* exclusive access to the bus */ + #define I2C_LOCK(adap) down(&adap->lock) +-#define I2C_UNLOCK(adap) up(&adap->lock) ++#define I2C_UNLOCK(adap) up(&adap->lock) + + #define ADAP_LOCK() down(&adap_lock) + #define ADAP_UNLOCK() up(&adap_lock) +@@ -67,7 +67,7 @@ + static int driver_count; + + /**** debug level */ +-static int i2c_debug=1; ++static int i2c_debug = 0; + + /* --------------------------------------------------- + * /proc entry declarations +@@ -77,14 +77,14 @@ + #ifdef CONFIG_PROC_FS + + static int i2cproc_init(void); +-static int i2cproc_cleanup(void); ++static void i2cproc_cleanup(void); + +-static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, ++static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, + loff_t *ppos); + static int read_bus_i2c(char *buf, char **start, off_t offset, int len, + int *eof , void *private); + +-/* To implement the dynamic /proc/bus/i2c-? files, we need our own ++/* To implement the dynamic /proc/bus/i2c-? files, we need our own + implementation of the read hook */ + static struct file_operations i2cproc_operations = { + read: i2cproc_bus_read, +@@ -101,8 +101,8 @@ + + + /* --------------------------------------------------- +- * registering functions +- * --------------------------------------------------- ++ * registering functions ++ * --------------------------------------------------- + */ + + /* ----- +@@ -119,7 +119,7 @@ + if (NULL == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { +- printk(KERN_WARNING ++ printk(KERN_WARNING + " i2c-core.o: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n", + adap->name); + res = -ENOMEM; +@@ -129,7 +129,7 @@ + adapters[i] = adap; + adap_count++; + ADAP_UNLOCK(); +- ++ + /* init data types */ + init_MUTEX(&adap->lock); + +@@ -157,18 +157,18 @@ + #endif /* def CONFIG_PROC_FS */ + + /* inform drivers of new adapters */ +- DRV_LOCK(); ++ DRV_LOCK(); + for (j=0;jflags&(I2C_DF_NOTIFY|I2C_DF_DUMMY))) + /* We ignore the return code; if it fails, too bad */ + drivers[j]->attach_adapter(adap); + DRV_UNLOCK(); +- ++ + DEB(printk(KERN_DEBUG "i2c-core.o: adapter %s registered as adapter %d.\n", + adap->name,i)); + +- return 0; ++ return 0; + + + ERROR1: +@@ -203,13 +203,13 @@ + * this or hell will break loose... + */ + DRV_LOCK(); +- for (j = 0; j < I2C_DRIVER_MAX; j++) ++ for (j = 0; j < I2C_DRIVER_MAX; j++) + if (drivers[j] && (drivers[j]->flags & I2C_DF_DUMMY)) + if ((res = drivers[j]->attach_adapter(adap))) { + printk(KERN_WARNING "i2c-core.o: can't detach adapter %s " + "while detaching driver %s: driver not " + "detached!",adap->name,drivers[j]->name); +- goto ERROR1; ++ goto ERROR1; + } + DRV_UNLOCK(); + +@@ -241,8 +241,8 @@ + + adapters[i] = NULL; + adap_count--; +- +- ADAP_UNLOCK(); ++ ++ ADAP_UNLOCK(); + DEB(printk(KERN_DEBUG "i2c-core.o: adapter unregistered: %s\n",adap->name)); + return 0; + +@@ -269,7 +269,7 @@ + if (NULL == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { +- printk(KERN_WARNING ++ printk(KERN_WARNING + " i2c-core.o: register_driver(%s) " + "- enlarge I2C_DRIVER_MAX.\n", + driver->name); +@@ -279,11 +279,11 @@ + + drivers[i] = driver; + driver_count++; +- ++ + DRV_UNLOCK(); /* driver was successfully added */ +- ++ + DEB(printk(KERN_DEBUG "i2c-core.o: driver %s registered.\n",driver->name)); +- ++ + ADAP_LOCK(); + + /* now look for instances of driver on our adapters +@@ -314,11 +314,11 @@ + return -ENODEV; + } + /* Have a look at each adapter, if clients of this driver are still +- * attached. If so, detach them to be able to kill the driver ++ * attached. If so, detach them to be able to kill the driver + * afterwards. + */ + DEB2(printk(KERN_DEBUG "i2c-core.o: unregister_driver - looking for clients.\n")); +- /* removing clients does not depend on the notify flag, else ++ /* removing clients does not depend on the notify flag, else + * invalid operation might (will!) result, when using stale client + * pointers. + */ +@@ -333,7 +333,7 @@ + /* DUMMY drivers do not register their clients, so we have to + * use a trick here: we call driver->attach_adapter to + * *detach* it! Of course, each dummy driver should know about +- * this or hell will break loose... ++ * this or hell will break loose... + */ + if ((res = driver->attach_adapter(adap))) { + printk(KERN_WARNING "i2c-core.o: while unregistering " +@@ -345,9 +345,9 @@ + return res; + } + } else { +- for (j=0;jclients[j]; +- if (client != NULL && ++ if (client != NULL && + client->driver == driver) { + DEB2(printk(KERN_DEBUG "i2c-core.o: " + "detaching client %s:\n", +@@ -376,7 +376,7 @@ + drivers[i] = NULL; + driver_count--; + DRV_UNLOCK(); +- ++ + DEB(printk(KERN_DEBUG "i2c-core.o: driver unregistered: %s\n",driver->name)); + return 0; + } +@@ -384,7 +384,7 @@ + int i2c_check_addr (struct i2c_adapter *adapter, int addr) + { + int i; +- for (i = 0; i < I2C_CLIENT_MAX ; i++) ++ for (i = 0; i < I2C_CLIENT_MAX ; i++) + if (adapter->clients[i] && (adapter->clients[i]->addr == addr)) + return -EBUSY; + return 0; +@@ -402,7 +402,7 @@ + if (NULL == adapter->clients[i]) + break; + if (I2C_CLIENT_MAX == i) { +- printk(KERN_WARNING ++ printk(KERN_WARNING + " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", + client->name); + return -ENOMEM; +@@ -410,9 +410,9 @@ + + adapter->clients[i] = client; + adapter->client_count++; +- +- if (adapter->client_register) +- if (adapter->client_register(client)) ++ ++ if (adapter->client_register) ++ if (adapter->client_register(client)) + printk(KERN_DEBUG "i2c-core.o: warning: client_register seems " + "to have failed for client %02x at adapter %s\n", + client->addr,adapter->name); +@@ -421,7 +421,7 @@ + + if(client->flags & I2C_CLIENT_ALLOW_USE) + client->usage_count = 0; +- ++ + return 0; + } + +@@ -440,12 +440,12 @@ + client->name); + return -ENODEV; + } +- +- if( (client->flags & I2C_CLIENT_ALLOW_USE) && ++ ++ if( (client->flags & I2C_CLIENT_ALLOW_USE) && + (client->usage_count>0)) + return -EBUSY; +- +- if (adapter->client_unregister != NULL) ++ ++ if (adapter->client_unregister != NULL) + if ((res = adapter->client_unregister(client))) { + printk(KERN_ERR "i2c-core.o: client_unregister [%s] failed, " + "client not detached",client->name); +@@ -471,7 +471,7 @@ + + void i2c_dec_use_client(struct i2c_client *client) + { +- ++ + if (client->driver->dec_use != NULL) + client->driver->dec_use(client); + +@@ -479,65 +479,65 @@ + client->adapter->dec_use(client->adapter); + } + +-struct i2c_client *i2c_get_client(int driver_id, int adapter_id, ++struct i2c_client *i2c_get_client(int driver_id, int adapter_id, + struct i2c_client *prev) + { + int i,j; +- ++ + /* Will iterate through the list of clients in each adapter of adapters-list +- in search for a client that matches the search criteria. driver_id or +- adapter_id are ignored if set to 0. If both are ignored this returns ++ in search for a client that matches the search criteria. driver_id or ++ adapter_id are ignored if set to 0. If both are ignored this returns + first client found. */ +- +- i = j = 0; +- +- /* set starting point */ ++ ++ i = j = 0; ++ ++ /* set starting point */ + if(prev) + { + if(!(prev->adapter)) + return (struct i2c_client *) -EINVAL; +- ++ + for(j=0; j < I2C_ADAP_MAX; j++) + if(prev->adapter == adapters[j]) + break; +- ++ + /* invalid starting point? */ + if (I2C_ADAP_MAX == j) { + printk(KERN_WARNING " i2c-core.o: get_client adapter for client:[%s] not found\n", + prev->name); + return (struct i2c_client *) -ENODEV; +- } +- ++ } ++ + for(i=0; i < I2C_CLIENT_MAX; i++) + if(prev == adapters[j]->clients[i]) + break; +- ++ + /* invalid starting point? */ + if (I2C_CLIENT_MAX == i) { + printk(KERN_WARNING " i2c-core.o: get_client client:[%s] not found\n", + prev->name); + return (struct i2c_client *) -ENODEV; +- } +- ++ } ++ + i++; /* start from one after prev */ + } +- ++ + for(; j < I2C_ADAP_MAX; j++) + { + if(!adapters[j]) + continue; +- ++ + if(adapter_id && (adapters[j]->id != adapter_id)) + continue; +- ++ + for(; i < I2C_CLIENT_MAX; i++) + { + if(!adapters[j]->clients[i]) + continue; +- ++ + if(driver_id && (adapters[j]->clients[i]->driver->id != driver_id)) + continue; +- if(adapters[j]->clients[i]->flags & I2C_CLIENT_ALLOW_USE) ++ if(adapters[j]->clients[i]->flags & I2C_CLIENT_ALLOW_USE) + return adapters[j]->clients[i]; + } + i = 0; +@@ -549,12 +549,12 @@ + int i2c_use_client(struct i2c_client *client) + { + if(client->flags & I2C_CLIENT_ALLOW_USE) { +- if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE) ++ if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE) + client->usage_count++; + else { +- if(client->usage_count > 0) ++ if(client->usage_count > 0) + return -EBUSY; +- else ++ else + client->usage_count++; + } + } +@@ -575,9 +575,9 @@ + return -EPERM; + } + } +- ++ + i2c_dec_use_client(client); +- ++ + return 0; + } + +@@ -589,7 +589,7 @@ + #ifdef CONFIG_PROC_FS + + /* This function generates the output for /proc/bus/i2c */ +-int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, ++int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, + void *private) + { + int i; +@@ -615,7 +615,7 @@ + } + + /* This function generates the output for /proc/bus/i2c-? */ +-ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, ++ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, + loff_t *ppos) + { + struct inode * inode = file->f_dentry->d_inode; +@@ -626,7 +626,7 @@ + int order[I2C_CLIENT_MAX]; + + if (count > 4000) +- return -EINVAL; ++ return -EINVAL; + len_total = file->f_pos + count; + /* Too bad if this gets longer (unlikely) */ + if (len_total > 4000) +@@ -641,12 +641,12 @@ + sorted by address */ + order_nr=0; + for (j = 0; j < I2C_CLIENT_MAX; j++) { +- if ((client = adapters[i]->clients[j]) && ++ if ((client = adapters[i]->clients[j]) && + (client->driver->id != I2C_DRIVERID_I2CDEV)) { +- for(k = order_nr; +- (k > 0) && ++ for(k = order_nr; ++ (k > 0) && + adapters[i]->clients[order[k-1]]-> +- addr > client->addr; ++ addr > client->addr; + k--) + order[k] = order[k-1]; + order[k] = j; +@@ -665,7 +665,7 @@ + len = len - file->f_pos; + if (len > count) + len = count; +- if (len < 0) ++ if (len < 0) + len = 0; + if (copy_to_user (buf,kbuf+file->f_pos, len)) { + kfree(kbuf); +@@ -689,7 +689,7 @@ + printk("i2c-core.o: /proc/bus/ does not exist"); + i2cproc_cleanup(); + return -ENOENT; +- } ++ } + proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); + if (!proc_bus_i2c) { + printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c"); +@@ -702,14 +702,13 @@ + return 0; + } + +-int i2cproc_cleanup(void) ++static void i2cproc_cleanup(void) + { + + if (i2cproc_initialized >= 1) { + remove_proc_entry("i2c",proc_bus); + i2cproc_initialized -= 2; + } +- return 0; + } + + +@@ -751,10 +750,10 @@ + msg.flags = client->flags & I2C_M_TEN; + msg.len = count; + (const char *)msg.buf = buf; +- ++ + DEB2(printk(KERN_DEBUG "i2c-core.o: master_send: writing %d bytes on %s.\n", + count,client->adapter->name)); +- ++ + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,&msg,1); + I2C_UNLOCK(adap); +@@ -784,14 +783,14 @@ + + DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: reading %d bytes on %s.\n", + count,client->adapter->name)); +- ++ + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,&msg,1); + I2C_UNLOCK(adap); +- ++ + DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n", + ret, count, client->addr)); +- ++ + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ +@@ -852,7 +851,7 @@ + found = 0; + + for (i = 0; !found && (address_data->force[i] != I2C_CLIENT_END); i += 3) { +- if (((adap_id == address_data->force[i]) || ++ if (((adap_id == address_data->force[i]) || + (address_data->force[i] == ANY_I2C_BUS)) && + (addr == address_data->force[i+1])) { + DEB2(printk(KERN_DEBUG "i2c-core.o: found force parameter for adapter %d, addr %04x\n", +@@ -862,7 +861,7 @@ + found = 1; + } + } +- if (found) ++ if (found) + continue; + + /* If this address is in one of the ignores, we can forget about +@@ -870,7 +869,7 @@ + for (i = 0; + !found && (address_data->ignore[i] != I2C_CLIENT_END); + i += 2) { +- if (((adap_id == address_data->ignore[i]) || ++ if (((adap_id == address_data->ignore[i]) || + ((address_data->ignore[i] == ANY_I2C_BUS))) && + (addr == address_data->ignore[i+1])) { + DEB2(printk(KERN_DEBUG "i2c-core.o: found ignore parameter for adapter %d, " +@@ -890,11 +889,11 @@ + found = 1; + } + } +- if (found) ++ if (found) + continue; + +- /* Now, we will do a detection, but only if it is in the normal or +- probe entries */ ++ /* Now, we will do a detection, but only if it is in the normal or ++ probe entries */ + for (i = 0; + !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); + i += 1) { +@@ -939,7 +938,7 @@ + "addr %04x\n", adap_id,addr)); + } + } +- if (!found) ++ if (!found) + continue; + + /* OK, so we really should examine this address. First check +@@ -1087,11 +1086,11 @@ + I2C_SMBUS_I2C_BLOCK_DATA,&data); + } + +-/* Simulate a SMBus command using the i2c protocol ++/* Simulate a SMBus command using the i2c protocol + No checking of parameters is done! */ +-static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, ++static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, + unsigned short flags, +- char read_write, u8 command, int size, ++ char read_write, u8 command, int size, + union i2c_smbus_data * data) + { + /* So we need to generate a series of msgs. In the case of writing, we +@@ -1101,7 +1100,7 @@ + unsigned char msgbuf0[34]; + unsigned char msgbuf1[34]; + int num = read_write == I2C_SMBUS_READ?2:1; +- struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, ++ struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, + { addr, flags | I2C_M_RD, 0, msgbuf1 } + }; + int i; +@@ -1179,7 +1178,7 @@ + case I2C_SMBUS_BYTE_DATA: + data->byte = msgbuf1[0]; + break; +- case I2C_SMBUS_WORD_DATA: ++ case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = msgbuf1[0] | (msgbuf1[1] << 8); + break; +@@ -1189,7 +1188,7 @@ + + + s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, +- char read_write, u8 command, int size, ++ char read_write, u8 command, int size, + union i2c_smbus_data * data) + { + s32 res; +@@ -1207,7 +1206,7 @@ + + + /* You should always define `functionality'; the 'else' is just for +- backward compatibility. */ ++ backward compatibility. */ + u32 i2c_get_functionality (struct i2c_adapter *adap) + { + if (adap->algo->functionality) +@@ -1233,122 +1232,12 @@ + + init_MUTEX(&adap_lock); + init_MUTEX(&driver_lock); +- +- i2cproc_init(); +- +- return 0; +-} + +-#ifndef MODULE +-#ifdef CONFIG_I2C_CHARDEV +- extern int i2c_dev_init(void); +-#endif +-#ifdef CONFIG_I2C_ALGOBIT +- extern int i2c_algo_bit_init(void); +-#endif +-#ifdef CONFIG_I2C_PHILIPSPAR +- extern int i2c_bitlp_init(void); +-#endif +-#ifdef CONFIG_I2C_ELV +- extern int i2c_bitelv_init(void); +-#endif +-#ifdef CONFIG_I2C_VELLEMAN +- extern int i2c_bitvelle_init(void); +-#endif +-#ifdef CONFIG_I2C_BITVIA +- extern int i2c_bitvia_init(void); +-#endif +- +-#ifdef CONFIG_I2C_ALGOPCF +- extern int i2c_algo_pcf_init(void); +-#endif +-#ifdef CONFIG_I2C_ELEKTOR +- extern int i2c_pcfisa_init(void); +-#endif +- +-#ifdef CONFIG_I2C_ALGO8XX +- extern int i2c_algo_8xx_init(void); +-#endif +-#ifdef CONFIG_I2C_RPXLITE +- extern int i2c_rpx_init(void); +-#endif +- +-#ifdef CONFIG_I2C_ALGO_SIBYTE +- extern int i2c_algo_sibyte_init(void); +- extern int i2c_sibyte_init(void); +-#endif +-#ifdef CONFIG_I2C_MAX1617 +- extern int i2c_max1617_init(void); +-#endif +- +-#ifdef CONFIG_I2C_PROC +- extern int sensors_init(void); +-#endif +- +-/* This is needed for automatic patch generation: sensors code starts here */ +-/* This is needed for automatic patch generation: sensors code ends here */ +- +-int __init i2c_init_all(void) +-{ +- /* --------------------- global ----- */ +- i2c_init(); +- +-#ifdef CONFIG_I2C_CHARDEV +- i2c_dev_init(); +-#endif +- /* --------------------- bit -------- */ +-#ifdef CONFIG_I2C_ALGOBIT +- i2c_algo_bit_init(); +-#endif +-#ifdef CONFIG_I2C_PHILIPSPAR +- i2c_bitlp_init(); +-#endif +-#ifdef CONFIG_I2C_ELV +- i2c_bitelv_init(); +-#endif +-#ifdef CONFIG_I2C_VELLEMAN +- i2c_bitvelle_init(); +-#endif +- +- /* --------------------- pcf -------- */ +-#ifdef CONFIG_I2C_ALGOPCF +- i2c_algo_pcf_init(); +-#endif +-#ifdef CONFIG_I2C_ELEKTOR +- i2c_pcfisa_init(); +-#endif +- +- /* --------------------- 8xx -------- */ +-#ifdef CONFIG_I2C_ALGO8XX +- i2c_algo_8xx_init(); +-#endif +-#ifdef CONFIG_I2C_RPXLITE +- i2c_rpx_init(); +-#endif +- +- /* --------------------- SiByte -------- */ +-#ifdef CONFIG_I2C_ALGO_SIBYTE +- i2c_algo_sibyte_init(); +- i2c_sibyte_init(); +-#endif +-#ifdef CONFIG_I2C_MAX1617 +- i2c_max1617_init(); +-#endif +- +- /* -------------- proc interface ---- */ +-#ifdef CONFIG_I2C_PROC +- sensors_init(); +-#endif +-/* This is needed for automatic patch generation: sensors code starts here */ +-/* This is needed for automatic patch generation: sensors code ends here */ ++ i2cproc_init(); + + return 0; + } + +-#endif +- +- +- + EXPORT_SYMBOL(i2c_add_adapter); + EXPORT_SYMBOL(i2c_del_adapter); + EXPORT_SYMBOL(i2c_add_driver); +@@ -1385,7 +1274,6 @@ + EXPORT_SYMBOL(i2c_get_functionality); + EXPORT_SYMBOL(i2c_check_functionality); + +-#ifdef MODULE + MODULE_AUTHOR("Simon G. Vogl "); + MODULE_DESCRIPTION("I2C-Bus main module"); + MODULE_LICENSE("GPL"); +@@ -1393,13 +1281,5 @@ + MODULE_PARM(i2c_debug, "i"); + MODULE_PARM_DESC(i2c_debug,"debug level"); + +-int init_module(void) +-{ +- return i2c_init(); +-} +- +-void cleanup_module(void) +-{ +- i2cproc_cleanup(); +-} +-#endif ++module_init(i2c_init); ++module_exit(i2cproc_cleanup); +diff -urN linux-2.4.26/drivers/i2c/i2c-dev.c linux-2.4.26-vrs1/drivers/i2c/i2c-dev.c +--- linux-2.4.26/drivers/i2c/i2c-dev.c 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/i2c/i2c-dev.c 2004-02-27 23:54:13.000000000 +0000 +@@ -1,5 +1,5 @@ + /* +- i2c-dev.c - i2c-bus driver, char device interface ++ i2c-dev.c - i2c-bus driver, char device interface + + Copyright (C) 1995-97 Simon G. Vogl + Copyright (C) 1998-99 Frodo Looijaard +@@ -25,7 +25,7 @@ + + /* The I2C_RDWR ioctl code is written by Kolja Waschk */ + +-/* The devfs code is contributed by Philipp Matthias Hahn ++/* The devfs code is contributed by Philipp Matthias Hahn + */ + + /* $Id: i2c-dev.c,v 1.40 2001/08/25 01:28:01 mds Exp $ */ +@@ -50,19 +50,14 @@ + #include + #include + +-#ifdef MODULE +-extern int init_module(void); +-extern int cleanup_module(void); +-#endif /* def MODULE */ +- + /* struct file_operations changed too often in the 2.1 series for nice code */ + +-static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, ++static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, + loff_t *offset); +-static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, ++static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, + loff_t *offset); + +-static int i2cdev_ioctl (struct inode *inode, struct file *file, ++static int i2cdev_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + static int i2cdev_open (struct inode *inode, struct file *file); + +@@ -73,13 +68,8 @@ + static int i2cdev_command(struct i2c_client *client, unsigned int cmd, + void *arg); + +-#ifdef MODULE +-static +-#else +-extern +-#endif +- int __init i2c_dev_init(void); +-static int i2cdev_cleanup(void); ++static int __init i2c_dev_init(void); ++static void i2cdev_cleanup(void); + + static struct file_operations i2cdev_fops = { + owner: THIS_MODULE, +@@ -185,7 +175,7 @@ + return ret; + } + +-int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, ++int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) + { + struct i2c_client *client = (struct i2c_client *)file->private_data; +@@ -198,14 +188,14 @@ + unsigned long funcs; + + #ifdef DEBUG +- printk(KERN_DEBUG "i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", ++ printk(KERN_DEBUG "i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", + MINOR(inode->i_rdev),cmd, arg); + #endif /* DEBUG */ + + switch ( cmd ) { + case I2C_SLAVE: + case I2C_SLAVE_FORCE: +- if ((arg > 0x3ff) || ++ if ((arg > 0x3ff) || + (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) + return -EINVAL; + if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg)) +@@ -224,8 +214,8 @@ + sizeof(unsigned long)))?-EFAULT:0; + + case I2C_RDWR: +- if (copy_from_user(&rdwr_arg, +- (struct i2c_rdwr_ioctl_data *)arg, ++ if (copy_from_user(&rdwr_arg, ++ (struct i2c_rdwr_ioctl_data *)arg, + sizeof(rdwr_arg))) + return -EFAULT; + +@@ -233,9 +223,9 @@ + * be sent at once */ + if (rdwr_arg.nmsgs > 42) + return -EINVAL; +- ++ + rdwr_pa = (struct i2c_msg *) +- kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), ++ kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), + GFP_KERNEL); + + if (rdwr_pa == NULL) return -ENOMEM; +@@ -312,9 +302,9 @@ + (struct i2c_smbus_ioctl_data *) arg, + sizeof(struct i2c_smbus_ioctl_data))) + return -EFAULT; +- if ((data_arg.size != I2C_SMBUS_BYTE) && ++ if ((data_arg.size != I2C_SMBUS_BYTE) && + (data_arg.size != I2C_SMBUS_QUICK) && +- (data_arg.size != I2C_SMBUS_BYTE_DATA) && ++ (data_arg.size != I2C_SMBUS_BYTE_DATA) && + (data_arg.size != I2C_SMBUS_WORD_DATA) && + (data_arg.size != I2C_SMBUS_PROC_CALL) && + (data_arg.size != I2C_SMBUS_BLOCK_DATA) && +@@ -325,9 +315,9 @@ + #endif + return -EINVAL; + } +- /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, ++ /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, + so the check is valid if size==I2C_SMBUS_QUICK too. */ +- if ((data_arg.read_write != I2C_SMBUS_READ) && ++ if ((data_arg.read_write != I2C_SMBUS_READ) && + (data_arg.read_write != I2C_SMBUS_WRITE)) { + #ifdef DEBUG + printk(KERN_DEBUG "i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n", +@@ -339,7 +329,7 @@ + /* Note that command values are always valid! */ + + if ((data_arg.size == I2C_SMBUS_QUICK) || +- ((data_arg.size == I2C_SMBUS_BYTE) && ++ ((data_arg.size == I2C_SMBUS_BYTE) && + (data_arg.read_write == I2C_SMBUS_WRITE))) + /* These are special: we do not use data */ + return i2c_smbus_xfer(client->adapter, client->addr, +@@ -358,13 +348,13 @@ + if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || + (data_arg.size == I2C_SMBUS_BYTE)) + datasize = sizeof(data_arg.data->byte); +- else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || ++ else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || + (data_arg.size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data_arg.data->word); + else /* size == I2C_SMBUS_BLOCK_DATA */ + datasize = sizeof(data_arg.data->block); + +- if ((data_arg.size == I2C_SMBUS_PROC_CALL) || ++ if ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_WRITE)) { + if (copy_from_user(&temp, data_arg.data, datasize)) + return -EFAULT; +@@ -372,7 +362,7 @@ + res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, + data_arg.read_write, + data_arg.command,data_arg.size,&temp); +- if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || ++ if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_READ))) { + if (copy_to_user(data_arg.data, &temp, datasize)) + return -EFAULT; +@@ -479,7 +469,7 @@ + return -1; + } + +-int __init i2c_dev_init(void) ++static int __init i2c_dev_init(void) + { + int res; + +@@ -509,7 +499,7 @@ + return 0; + } + +-int i2cdev_cleanup(void) ++static void i2cdev_cleanup(void) + { + int res; + +@@ -517,9 +507,9 @@ + if ((res = i2c_del_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver deregistration failed, " + "module not removed.\n"); +- return res; ++ return; + } +- i2cdev_initialized --; ++ i2cdev_initialized --; + } + + if (i2cdev_initialized >= 1) { +@@ -531,30 +521,17 @@ + #endif + printk("i2c-dev.o: unable to release major %d for i2c bus\n", + I2C_MAJOR); +- return res; ++ return; + } + i2cdev_initialized --; + } +- return 0; + } + + EXPORT_NO_SYMBOLS; + +-#ifdef MODULE +- + MODULE_AUTHOR("Frodo Looijaard and Simon G. Vogl "); + MODULE_DESCRIPTION("I2C /dev entries driver"); + MODULE_LICENSE("GPL"); + +-int init_module(void) +-{ +- return i2c_dev_init(); +-} +- +-int cleanup_module(void) +-{ +- return i2cdev_cleanup(); +-} +- +-#endif /* def MODULE */ +- ++module_init(i2c_dev_init); ++module_exit(i2cdev_cleanup); +diff -urN linux-2.4.26/drivers/i2c/i2c-elektor.c linux-2.4.26-vrs1/drivers/i2c/i2c-elektor.c +--- linux-2.4.26/drivers/i2c/i2c-elektor.c 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/i2c/i2c-elektor.c 2004-02-23 13:36:30.000000000 +0000 +@@ -181,16 +181,12 @@ + + static void pcf_isa_inc_use(struct i2c_adapter *adap) + { +-#ifdef MODULE + MOD_INC_USE_COUNT; +-#endif + } + + static void pcf_isa_dec_use(struct i2c_adapter *adap) + { +-#ifdef MODULE + MOD_DEC_USE_COUNT; +-#endif + } + + +@@ -219,7 +215,7 @@ + pcf_isa_unreg, + }; + +-int __init i2c_pcfisa_init(void) ++static int __init i2c_pcfisa_init(void) + { + #ifdef __alpha__ + /* check to see we have memory mapped PCF8584 connected to the +@@ -289,10 +285,14 @@ + return 0; + } + ++static void i2c_pcfisa_exit(void) ++{ ++ i2c_pcf_del_bus(&pcf_isa_ops); ++ pcf_isa_exit(); ++} + + EXPORT_NO_SYMBOLS; + +-#ifdef MODULE + MODULE_AUTHOR("Hans Berglund "); + MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); + MODULE_LICENSE("GPL"); +@@ -304,15 +304,5 @@ + MODULE_PARM(mmapped, "i"); + MODULE_PARM(i2c_debug, "i"); + +-int init_module(void) +-{ +- return i2c_pcfisa_init(); +-} +- +-void cleanup_module(void) +-{ +- i2c_pcf_del_bus(&pcf_isa_ops); +- pcf_isa_exit(); +-} +- +-#endif ++module_init(i2c_pcfisa_init); ++module_exit(i2c_pcfisa_exit); +diff -urN linux-2.4.26/drivers/i2c/i2c-elv.c linux-2.4.26-vrs1/drivers/i2c/i2c-elv.c +--- linux-2.4.26/drivers/i2c/i2c-elv.c 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/i2c/i2c-elv.c 2004-02-27 23:52:23.000000000 +0000 +@@ -75,7 +75,7 @@ + PortData |=2; + } + outb(PortData, DATA); +-} ++} + + static int bit_elv_getscl(void *data) + { +@@ -90,7 +90,7 @@ + static int bit_elv_init(void) + { + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { +- return -ENODEV; ++ return -ENODEV; + } else { + /* test for ELV adap. */ + if (inb(base+1) & 0x80) { /* BUSY should be high */ +@@ -131,16 +131,12 @@ + + static void bit_elv_inc_use(struct i2c_adapter *adap) + { +-#ifdef MODULE + MOD_INC_USE_COUNT; +-#endif + } + + static void bit_elv_dec_use(struct i2c_adapter *adap) + { +-#ifdef MODULE + MOD_DEC_USE_COUNT; +-#endif + } + + /* ------------------------------------------------------------------------ +@@ -164,10 +160,10 @@ + bit_elv_inc_use, + bit_elv_dec_use, + bit_elv_reg, +- bit_elv_unreg, ++ bit_elv_unreg, + }; + +-int __init i2c_bitelv_init(void) ++static int __init i2c_bitelv_init(void) + { + printk(KERN_INFO "i2c-elv.o: i2c ELV parallel port adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE); + if (base==0) { +@@ -194,24 +190,19 @@ + } + + ++static void __exit i2c_bitelv_exit(void) ++{ ++ i2c_bit_del_bus(&bit_elv_ops); ++ bit_elv_exit(); ++} ++ + EXPORT_NO_SYMBOLS; + +-#ifdef MODULE + MODULE_AUTHOR("Simon G. Vogl "); + MODULE_DESCRIPTION("I2C-Bus adapter routines for ELV parallel port adapter"); + MODULE_LICENSE("GPL"); + + MODULE_PARM(base, "i"); + +-int init_module(void) +-{ +- return i2c_bitelv_init(); +-} +- +-void cleanup_module(void) +-{ +- i2c_bit_del_bus(&bit_elv_ops); +- bit_elv_exit(); +-} +- +-#endif ++module_init(i2c_bitelv_init); ++module_exit(i2c_bitelv_exit); +diff -urN linux-2.4.26/drivers/i2c/i2c-frodo.c linux-2.4.26-vrs1/drivers/i2c/i2c-frodo.c +--- linux-2.4.26/drivers/i2c/i2c-frodo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/i2c/i2c-frodo.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,114 @@ ++ ++/* ++ * linux/drivers/i2c/i2c-frodo.c ++ * ++ * Author: Abraham van der Merwe ++ * ++ * An I2C adapter driver for the 2d3D, Inc. StrongARM SA-1110 ++ * Development board (Frodo). ++ * ++ * This source code is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++static void frodo_setsda (void *data,int state) ++{ ++ if (state) ++ frodo_cpld_set (FRODO_CPLD_I2C,FRODO_I2C_SDA_OUT); ++ else ++ frodo_cpld_clear (FRODO_CPLD_I2C,FRODO_I2C_SDA_OUT); ++} ++ ++static void frodo_setscl (void *data,int state) ++{ ++ if (state) ++ frodo_cpld_set (FRODO_CPLD_I2C,FRODO_I2C_SCL_OUT); ++ else ++ frodo_cpld_clear (FRODO_CPLD_I2C,FRODO_I2C_SCL_OUT); ++} ++ ++static int frodo_getsda (void *data) ++{ ++ return ((frodo_cpld_read (FRODO_CPLD_I2C) & FRODO_I2C_SDA_IN) != 0); ++} ++ ++static int frodo_getscl (void *data) ++{ ++ return ((frodo_cpld_read (FRODO_CPLD_I2C) & FRODO_I2C_SCL_IN) != 0); ++} ++ ++static struct i2c_algo_bit_data bit_frodo_data = { ++ setsda: frodo_setsda, ++ setscl: frodo_setscl, ++ getsda: frodo_getsda, ++ getscl: frodo_getscl, ++ udelay: 80, ++ mdelay: 80, ++ timeout: 100 ++}; ++ ++static int frodo_client_register (struct i2c_client *client) ++{ ++ return (0); ++} ++ ++static int frodo_client_unregister (struct i2c_client *client) ++{ ++ return (0); ++} ++ ++static void frodo_inc_use (struct i2c_adapter *adapter) ++{ ++ MOD_INC_USE_COUNT; ++} ++ ++static void frodo_dec_use (struct i2c_adapter *adapter) ++{ ++ MOD_DEC_USE_COUNT; ++} ++ ++static struct i2c_adapter frodo_ops = { ++ name: "Frodo adapter driver", ++ id: I2C_HW_B_FRODO, ++ algo: NULL, ++ algo_data: &bit_frodo_data, ++ inc_use: frodo_inc_use, ++ dec_use: frodo_dec_use, ++ client_register: frodo_client_register, ++ client_unregister: frodo_client_unregister ++}; ++ ++static int __init i2c_frodo_init (void) ++{ ++ return (i2c_bit_add_bus (&frodo_ops)); ++} ++ ++EXPORT_NO_SYMBOLS; ++ ++static void __exit i2c_frodo_exit (void) ++{ ++ i2c_bit_del_bus (&frodo_ops); ++} ++ ++MODULE_AUTHOR ("Abraham van der Merwe "); ++MODULE_DESCRIPTION ("I2C-Bus adapter routines for Frodo"); ++MODULE_LICENSE ("GPL"); ++EXPORT_NO_SYMBOLS; ++ ++module_init (i2c_frodo_init); ++module_exit (i2c_frodo_exit); ++ +diff -urN linux-2.4.26/drivers/i2c/i2c-guide.c linux-2.4.26-vrs1/drivers/i2c/i2c-guide.c +--- linux-2.4.26/drivers/i2c/i2c-guide.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/i2c/i2c-guide.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,199 @@ ++/************************************************************************************\ ++Copyright : Copyright (C) 1995-2000 Simon G. Vogl ++ Copyright 2002 IDERs Incorporated ++File Name : i2c-guide.c ++Description : this i2c driver uses the GPIO port B pin 0 and pin 1 on the cs89712. ++Notes : To change the bit rate, change the structure i2c_algo_bit_data ++ : to 10 10 100 ++Contact : tsong@iders.ca ++License : This source code is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ version 2 as published by the Free Software Foundation. ++\************************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include /* for 2.0 kernels to get NULL */ ++#include /* for 2.0 kernels to get ENODEV */ ++#include ++ ++#include // io operation ep_writel() ++#include // io operation clps_writel() ++#include // io operation clps_writel() ++ ++#include ++#include ++ ++/* ----- global defines ----------------------------------------------- */ ++ ++#define DEB(x) /* should be reasonable open, close &c. */ ++#define DEB2(x) /* low level debugging - very slow */ ++#define DEBE(x) x /* error messages */ ++ /* Pin Port Inverted name */ ++#define I2C_SDA 0x08 /* port B ctrl pin 3 (inv) */ ++#define I2C_SCL 0x04 /* port B ctrl pin 2 (inv) */ ++ ++#define I2C_SDAIN 0x08 /* use the same pin with output */ ++#define I2C_SCLIN 0x04 /* use the same pin with output */ ++ ++#define I2C_DMASK 0xf7 /* inverse of I2C_SDA */ ++#define I2C_CMASK 0xfb /* inverse of I2c_SCL */ ++ ++#define PORTB_PIN0_SDA_OUTPUT 0x08 /* pin 3 direction of port B output */ ++#define PORTB_PIN0_SDA_INPUT 0xf7 /* pin 3 direction of port B input */ ++ ++#define PORTB_PIN1_SCL_OUTPUT 0x04 /* pin 2 direction of port B output */ ++#define PORTB_PIN1_SCL_INPUT 0xfb /* pin 2 direction of port B input */ ++ ++int base = 0; ++#define DEFAULT_BASE PBDR ++ ++/* ----- local functions --------------------------------------------------- */ ++ ++static void bit_guide_setscl(void* data, int state) ++{ ++ if (state) { ++ // set port B pin2 input ++ clps_writeb((clps_readb(PBDDR)) & PORTB_PIN1_SCL_INPUT, PBDDR); ++ } ++ else { ++ // clear ++ clps_writeb((clps_readb(PBDR)) & I2C_CMASK, PBDR); ++ // set port B pin2 output ++ clps_writeb((clps_readb(PBDDR)) | PORTB_PIN1_SCL_OUTPUT, PBDDR); ++ } ++} ++ ++static void bit_guide_setsda(void* data, int state) ++{ ++ if (state) { ++ clps_writeb((clps_readb(PBDDR)) & PORTB_PIN0_SDA_INPUT, PBDDR); ++ // float pin 0 (actually drive high by pull up resistor) ++ // clps_writeb((clps_readb(PBDR)) | I2C_SDA, PBDR); // set Jan4 ori: eff ++ // printk("set sda high, state=%i\n",state); ++ } ++ else { ++ // clear ++ clps_writeb((clps_readb(PBDR)) & I2C_DMASK, PBDR); ++ // set port B pin 0 output ++ clps_writeb((clps_readb(PBDDR)) | PORTB_PIN0_SDA_OUTPUT, PBDDR); ++ } ++} ++ ++static int bit_guide_getscl(void *data) ++{ ++ return ( 0 != ( (clps_readb(PBDR)) & I2C_SCLIN ) ); ++} ++ ++static int bit_guide_getsda(void *data) ++{ ++ // set port B pin 0 input Jan4 ori eff ++ clps_writeb((clps_readb(PBDDR)) & PORTB_PIN0_SDA_INPUT, PBDDR); ++ return ( 0 != ( (clps_readb(PBDR) ) & I2C_SDAIN ) ); ++} ++ ++static int bit_guide_init(void) ++{ ++ bit_guide_setsda((void*)base,1); ++ bit_guide_setscl((void*)base,1); ++ return 0; ++} ++ ++static int bit_guide_reg(struct i2c_client *client) ++{ ++ return 0; ++} ++ ++static int bit_guide_unreg(struct i2c_client *client) ++{ ++ return 0; ++} ++ ++static void bit_guide_inc_use(struct i2c_adapter *adap) ++{ ++#ifdef MODULE ++ MOD_INC_USE_COUNT; ++#endif ++} ++ ++static void bit_guide_dec_use(struct i2c_adapter *adap) ++{ ++#ifdef MODULE ++ MOD_DEC_USE_COUNT; ++#endif ++} ++ ++/* ------------------------------------------------------------------------ ++ * Encapsulate the above functions in the correct operations structure. ++ * This is only done when more than one hardware adapter is supported. ++ */ ++ ++/* last line (us, ms, timout) ++ * us dominates the bit rate: 10us means: 100Kbit/sec(25 means 40kbps) ++ * 10ms not known ++ * 100ms timeout ++ */ ++static struct i2c_algo_bit_data bit_guide_data = { ++ NULL, ++ bit_guide_setsda, ++ bit_guide_setscl, ++ bit_guide_getsda, ++ bit_guide_getscl, ++ 50, 10, 100, /* orginal (non-guide) value 10, 10, 100 */ ++}; ++ ++static struct i2c_adapter bit_guide_ops = { ++ "Guide Port B: PIN2-SCL/PIN3-SDA", ++ I2C_HW_B_GUIDE, ++ NULL, ++ &bit_guide_data, ++ bit_guide_inc_use, ++ bit_guide_dec_use, ++ bit_guide_reg, ++ bit_guide_unreg, ++}; ++ ++static int __init i2c_bitguide_init(void) ++{ ++ printk("i2c-guide.o: Guide i2c port B adapter module.\n"); ++ clps_writeb((clps_readb(PBDDR)) & 0xfd, PBDDR); // set service reuest pb1 as input ++ if (base==0) { ++ /* probe some values */ ++ base=DEFAULT_BASE; ++ bit_guide_data.data=(void*)DEFAULT_BASE; ++ if (bit_guide_init()==0) { ++ if(i2c_bit_add_bus(&bit_guide_ops) < 0) ++ return -ENODEV; ++ } else { ++ return -ENODEV; ++ } ++ } else { ++ bit_guide_data.data=(void*)base; ++ if (bit_guide_init()==0) { ++ if(i2c_bit_add_bus(&bit_guide_ops) < 0) ++ return -ENODEV; ++ } else { ++ return -ENODEV; ++ } ++ } ++ printk("i2c-guide.o: found device at %#x.\n",base); ++ return 0; ++} ++ ++EXPORT_NO_SYMBOLS; ++ ++MODULE_AUTHOR("T. C. Song "); ++MODULE_DESCRIPTION("I2C-Bus adapter routines for Guide (cs89712) GPIO port B"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM(base, "i"); ++ ++module_init(i2c_bitguide_init); ++/* for completeness, we should have a module_exit() function, but the ++ GUIDE requires this to always be loaded. If it is unloaded, the ++ operation of the GUIDE is undefined. ++ Nobody has written the i2c_bitguide_exit() routine yet, so it is not included. ++module_exit(i2c_bitguide_exit); ++*/ +diff -urN linux-2.4.26/drivers/i2c/i2c-omaha.c linux-2.4.26-vrs1/drivers/i2c/i2c-omaha.c +--- linux-2.4.26/drivers/i2c/i2c-omaha.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/i2c/i2c-omaha.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,276 @@ ++/* ------------------------------------------------------------------------- * ++ Copyright ARM Limited 2002. All rights reserved. ++ ++ i2c driver for Omaha ++ ++ Notes:Based on i2c-elv.c ++ ++ The S3C2400X01 has better support for I2C, but bit oriented operations ++ are directly supported by the other I2C layers, so we use that method ++ of performing I2C operations. ++ ++ Copyright (C) 1995-2000 Simon G. Vogl ++ ++ 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ ++/* ------------------------------------------------------------------------- */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++/* ----- global defines ----------------------------------------------- */ ++#define DEB(x) if (i2c_debug>=1) x; ++#define DEB2(x) if (i2c_debug>=2) x; ++#define DEB3(x) if (i2c_debug>=3) x ++#define DEBE(x) x // error messages ++#define DEBSTAT(x) if (i2c_debug>=3) x; /* print several statistical values*/ ++#define DEBPROTO(x) if (i2c_debug>=9) { x; } ++ /* debug the protocol by showing transferred bits */ ++ ++/* Register and bitdefs for Omaha */ ++ ++// Port G control registers ++static volatile unsigned int pgcon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_PGCON); ++static volatile unsigned int pgdat = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_PGDAT); ++ ++static volatile unsigned int opencr = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_OPENCR); ++ ++static int base = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_PGCON); ++ ++// Open drain control registers ++#define OPC_CMD BIT2 ++#define OPC_DAT BIT3 ++ ++// data bits in GPIO Port G data register ++#define OMAHA_SDA BIT5 ++#define OMAHA_SCL BIT6 ++#define IIC_WP BIT3 // Write Protect for EEPROM ++ ++// input/out select bits in GPIO G control register ++#define IIC_BITS (BIT12|BIT10|BIT6); ++ ++ ++/* ----- local functions ---------------------------------------------- */ ++ ++ ++static void bit_omaha_setscl(void *data, int state) ++{ ++ unsigned int tmp; ++ ++ if (state) ++ { ++ tmp = __raw_readl(pgdat); ++ tmp |= OMAHA_SCL; ++ __raw_writel(tmp,pgdat); ++ } ++ else ++ { ++ tmp = __raw_readl(pgdat); ++ tmp &= ~OMAHA_SCL; ++ __raw_writel(tmp,pgdat); ++ } ++} ++ ++static void bit_omaha_setsda(void *data, int state) ++{ ++ unsigned int tmp; ++ ++ // ensure that sda is an output at the moment ++ tmp = __raw_readl(pgcon); ++ tmp = tmp | BIT10; ++ __raw_writel(tmp,pgcon); ++ ++ if (state) ++ { ++ tmp = __raw_readl(pgdat); ++ tmp |= OMAHA_SDA; ++ __raw_writel(tmp,pgdat); ++ } ++ else ++ { ++ tmp = __raw_readl(pgdat); ++ tmp &= ~OMAHA_SDA; ++ __raw_writel(tmp,pgdat); ++ } ++} ++ ++static int bit_omaha_getscl(void *data) ++{ ++ if (__raw_readl(pgdat) & OMAHA_SCL) ++ return 1; ++ else ++ return 0; ++} ++ ++static int bit_omaha_getsda(void *data) ++{ ++ unsigned int tmp; ++ ++ // ensure that sda is an output at the moment ++ tmp = __raw_readl(pgcon); ++ tmp = tmp & ~BIT10; ++ __raw_writel(tmp,pgcon); ++ ++ if (__raw_readl(pgdat) & OMAHA_SDA) ++ return 1; ++ else ++ return 0; ++} ++ ++static int bit_omaha_init(void) ++{ ++ // Have we got some mmapped space? ++ if (request_region(base, 0x100, "i2c (omaha bus adapter)") < 0 ) ++ { ++ printk("i2c-omaha.o: requested I/O region (0x%08x) is in use.\n", base); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++ ++static int bit_omaha_reg(struct i2c_client *client) ++{ ++ return 0; ++} ++ ++ ++static int bit_omaha_unreg(struct i2c_client *client) ++{ ++ return 0; ++} ++ ++static void bit_omaha_inc_use(struct i2c_adapter *adap) ++{ ++ MOD_INC_USE_COUNT; ++} ++ ++static void bit_omaha_dec_use(struct i2c_adapter *adap) ++{ ++ MOD_DEC_USE_COUNT; ++} ++ ++ ++ ++/* ------------------------------------------------------------------------ ++ * Encapsulate the above functions in the correct operations structure. ++ * This is only done when more than one hardware adapter is supported. ++ */ ++static struct i2c_algo_bit_data bit_omaha_data = { ++ NULL, ++ bit_omaha_setsda, ++ bit_omaha_setscl, ++ bit_omaha_getsda, ++ bit_omaha_getscl, ++ 10, 10, 20, /* waits, timeout */ ++}; ++ ++static struct i2c_adapter bit_omaha_ops = { ++ "BIT-Type Omaha I2C adapter", ++ I2C_HW_B_OMAHA, ++ NULL, ++ &bit_omaha_data, ++ bit_omaha_inc_use, ++ bit_omaha_dec_use, ++ bit_omaha_reg, ++ bit_omaha_unreg, ++}; ++ ++static int __init i2c_omaha_init (void) ++{ ++ unsigned int tmp; ++ ++ printk("i2c-omaha.o: i2c omaha adapter module\n"); ++ ++ if (bit_omaha_init() == 0) { ++ if(i2c_bit_add_bus(&bit_omaha_ops) < 0) ++ { ++ printk("Could not add bus!\n"); ++ return -ENODEV; ++ } ++ } else { ++ printk("Could not pcf_omaha_init\n"); ++ return -ENODEV; ++ } ++ ++ // Program Port G bits to output function ++ tmp = __raw_readl(pgcon); ++ tmp |= IIC_BITS; ++ __raw_writel(tmp,pgcon); ++ ++ // Ensure SDA and SCL are open-drain ++ tmp = __raw_readl(opencr); ++ tmp = tmp | OPC_CMD | OPC_DAT; ++ __raw_writel(tmp,opencr); ++ ++ bit_omaha_setsda((void*)base,1); ++ bit_omaha_setscl((void*)base,1); ++ ++ // Disable WP ++ tmp = __raw_readl(pgdat); ++ tmp = tmp & ~IIC_WP; ++ __raw_writel(tmp,pgdat); ++ ++ return 0; ++} ++ ++static void bit_omaha_exit(void) ++{ ++ release_region(base , 2); ++} ++ ++static void i2c_omaha_exit(void) ++{ ++ ++ i2c_bit_del_bus(&bit_omaha_ops); ++ ++ bit_omaha_exit(); ++ ++} ++ ++EXPORT_NO_SYMBOLS; ++ ++MODULE_AUTHOR("ARM Limited "); ++MODULE_DESCRIPTION("I2C-Bus adapter routines for Omaha"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM(base, "i"); ++MODULE_PARM(irq, "i"); ++MODULE_PARM(clock, "i"); ++MODULE_PARM(own, "i"); ++MODULE_PARM(mmapped, "i"); ++MODULE_PARM(i2c_debug, "i"); ++ ++ ++module_init(i2c_omaha_init); ++module_exit(i2c_omaha_exit); ++ ++ +diff -urN linux-2.4.26/drivers/i2c/i2c-philips-par.c linux-2.4.26-vrs1/drivers/i2c/i2c-philips-par.c +--- linux-2.4.26/drivers/i2c/i2c-philips-par.c 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/i2c/i2c-philips-par.c 2004-02-27 23:50:14.000000000 +0000 +@@ -16,7 +16,7 @@ + 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ +-/* ------------------------------------------------------------------------- */ ++/* ------------------------------------------------------------------------- */ + + /* With some changes from Kyƶsti MƤlkki and even + Frodo Looijaard */ +@@ -72,7 +72,7 @@ + + static void bit_lp_setscl(void *data, int state) + { +- /*be cautious about state of the control register - ++ /*be cautious about state of the control register - + touch only the one bit needed*/ + if (state) { + parport_write_control((struct parport *) data, +@@ -126,7 +126,7 @@ + + static int bit_lp_getsda2(void *data) + { +- return (parport_read_status((struct parport *) data) & ++ return (parport_read_status((struct parport *) data) & + PARPORT_STATUS_BUSY) ? 0 : 1; + } + +@@ -154,7 +154,7 @@ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +- ++ + static struct i2c_algo_bit_data bit_lp_data = { + NULL, + bit_lp_setsda, +@@ -162,7 +162,7 @@ + bit_lp_getsda, + bit_lp_getscl, + 80, 80, 100, /* waits, timeout */ +-}; ++}; + + static struct i2c_algo_bit_data bit_lp_data2 = { + NULL, +@@ -171,7 +171,7 @@ + bit_lp_getsda2, + NULL, + 80, 80, 100, /* waits, timeout */ +-}; ++}; + + static struct i2c_adapter bit_lp_ops = { + "Philips Parallel port adapter", +@@ -197,7 +197,7 @@ + printk(KERN_DEBUG "i2c-philips-par.o: attaching to %s\n", port->name); + + adapter->pdev = parport_register_device(port, "i2c-philips-par", +- NULL, NULL, NULL, ++ NULL, NULL, NULL, + PARPORT_FLAG_EXCL, + NULL); + if (!adapter->pdev) { +@@ -257,16 +257,16 @@ + NULL + }; + +-int __init i2c_bitlp_init(void) ++static int __init i2c_bitlp_init(void) + { + printk(KERN_INFO "i2c-philips-par.o: i2c Philips parallel port adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE); + + parport_register_driver(&i2c_driver); +- ++ + return 0; + } + +-void __exit i2c_bitlp_exit(void) ++static void __exit i2c_bitlp_exit(void) + { + parport_unregister_driver(&i2c_driver); + } +@@ -279,14 +279,5 @@ + + MODULE_PARM(type, "i"); + +-#ifdef MODULE +-int init_module(void) +-{ +- return i2c_bitlp_init(); +-} +- +-void cleanup_module(void) +-{ +- i2c_bitlp_exit(); +-} +-#endif ++module_init(i2c_bitlp_init); ++module_exit(i2c_bitlp_exit); +diff -urN linux-2.4.26/drivers/i2c/i2c-velleman.c linux-2.4.26-vrs1/drivers/i2c/i2c-velleman.c +--- linux-2.4.26/drivers/i2c/i2c-velleman.c 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/i2c/i2c-velleman.c 2004-02-27 23:46:12.000000000 +0000 +@@ -65,7 +65,7 @@ + } else { + outb(inb(CTRL) | I2C_SCL, CTRL); + } +- ++ + } + + static void bit_velle_setsda(void *data, int state) +@@ -75,8 +75,8 @@ + } else { + outb(inb(CTRL) | I2C_SDA, CTRL); + } +- +-} ++ ++} + + static int bit_velle_getscl(void *data) + { +@@ -95,7 +95,7 @@ + base)); + return -ENODEV; + } else { +- request_region(base, (base == 0x3bc)? 3 : 8, ++ request_region(base, (base == 0x3bc)? 3 : 8, + "i2c (Vellemann adapter)"); + bit_velle_setsda((void*)base,1); + bit_velle_setscl((void*)base,1); +@@ -104,7 +104,7 @@ + } + + static void __exit bit_velle_exit(void) +-{ ++{ + release_region( base , (base == 0x3bc)? 3 : 8 ); + } + +@@ -121,16 +121,12 @@ + + static void bit_velle_inc_use(struct i2c_adapter *adap) + { +-#ifdef MODULE + MOD_INC_USE_COUNT; +-#endif + } + + static void bit_velle_dec_use(struct i2c_adapter *adap) + { +-#ifdef MODULE + MOD_DEC_USE_COUNT; +-#endif + } + + /* ------------------------------------------------------------------------ +@@ -158,7 +154,7 @@ + bit_velle_unreg, + }; + +-int __init i2c_bitvelle_init(void) ++static int __init i2c_bitvelle_init(void) + { + printk(KERN_INFO "i2c-velleman.o: i2c Velleman K8000 adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE); + if (base==0) { +@@ -184,24 +180,19 @@ + return 0; + } + ++static void __exit i2c_bitvelle_exit(void) ++{ ++ i2c_bit_del_bus(&bit_velle_ops); ++ bit_velle_exit(); ++} ++ + EXPORT_NO_SYMBOLS; + +-#ifdef MODULE + MODULE_AUTHOR("Simon G. Vogl "); + MODULE_DESCRIPTION("I2C-Bus adapter routines for Velleman K8000 adapter"); + MODULE_LICENSE("GPL"); + + MODULE_PARM(base, "i"); + +-int init_module(void) +-{ +- return i2c_bitvelle_init(); +-} +- +-void cleanup_module(void) +-{ +- i2c_bit_del_bus(&bit_velle_ops); +- bit_velle_exit(); +-} +- +-#endif ++module_init(i2c_bitvelle_init); ++module_exit(i2c_bitvelle_exit); +diff -urN linux-2.4.26/drivers/ide/Config.in linux-2.4.26-vrs1/drivers/ide/Config.in +--- linux-2.4.26/drivers/ide/Config.in 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ide/Config.in 2004-04-18 21:47:50.000000000 +0100 +@@ -107,6 +107,9 @@ + define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_ICS + dep_bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_ARCH_ACORN + fi ++ if [ "$CONFIG_ARCH_RISCSTATION" = "y" ]; then ++ dep_bool ' RiscStation IDE' CONFIG_BLK_DEV_IDE_RISCSTATION $CONFIG_ARCH_RISCSTATION ++ fi + if [ "$CONFIG_AMIGA" = "y" ]; then + dep_bool ' Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE $CONFIG_AMIGA + dep_mbool ' Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE $CONFIG_EXPERIMENTAL +diff -urN linux-2.4.26/drivers/ide/arm/Makefile linux-2.4.26-vrs1/drivers/ide/arm/Makefile +--- linux-2.4.26/drivers/ide/arm/Makefile 2003-06-13 15:51:33.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ide/arm/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -6,6 +6,7 @@ + + obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o + obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o ++obj-$(CONFIG_BLK_DEV_IDE_RISCSTATION) += rstation-ide.o + + EXTRA_CFLAGS := -I../ + +diff -urN linux-2.4.26/drivers/ide/arm/icside.c linux-2.4.26-vrs1/drivers/ide/arm/icside.c +--- linux-2.4.26/drivers/ide/arm/icside.c 2003-06-13 15:51:33.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ide/arm/icside.c 2004-01-14 21:32:25.000000000 +0000 +@@ -1,7 +1,7 @@ + /* + * linux/drivers/ide/arm/icside.c + * +- * Copyright (c) 1996,1997 Russell King. ++ * Copyright (c) 1996-2003 Russell King. + * + * Changelog: + * 08-Jun-1996 RMK Created +@@ -26,24 +26,6 @@ + #include + #include + +-#include "ide-noise.h" +- +-/* +- * FIXME: We want to drop the the MACRO CRAP! +- * +- * ec->iops->in{b/w/l} +- * ec->iops->in{b/w/l}_p +- * ec->iops->out{b/w/l} +- * ec->iops->out{b/w/l}_p +- * +- * the new core supports clean MMIO calls and other goodies +- */ +- +-/* +- * Maximum number of interfaces per card +- */ +-#define MAX_IFS 2 +- + #define ICS_IDENT_OFFSET 0x8a0 + + #define ICS_ARCIN_V5_INTRSTAT 0x000 +@@ -86,17 +68,20 @@ + ICS_ARCIN_V6_IDESTEPPING + }; + +-static const card_ids icside_cids[] = { +- { MANU_ICS, PROD_ICS_IDE }, +- { MANU_ICS2, PROD_ICS2_IDE }, +- { 0xffff, 0xffff } ++struct icside_state { ++ unsigned int channel; ++ unsigned int enabled; ++ unsigned long irq_port; ++ unsigned long slot_port; ++ unsigned int type; ++ ide_hwif_t *hwif[2]; + }; + +-typedef enum { +- ics_if_unknown, +- ics_if_arcin_v5, +- ics_if_arcin_v6 +-} iftype_t; ++#define ICS_TYPE_A3IN 0 ++#define ICS_TYPE_A3USER 1 ++#define ICS_TYPE_V6 3 ++#define ICS_TYPE_V5 15 ++#define ICS_TYPE_NOTYPE ((unsigned int)-1) + + /* ---------------- Version 5 PCB Support Functions --------------------- */ + /* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) +@@ -104,8 +89,10 @@ + */ + static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) + { +- unsigned int memc_port = (unsigned int)ec->irq_data; +- outb(0, memc_port + ICS_ARCIN_V5_INTROFFSET); ++ struct icside_state *state = ec->irq_data; ++ unsigned int base = state->irq_port; ++ ++ outb(0, base + ICS_ARCIN_V5_INTROFFSET); + } + + /* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) +@@ -113,17 +100,15 @@ + */ + static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) + { +- unsigned int memc_port = (unsigned int)ec->irq_data; +- inb(memc_port + ICS_ARCIN_V5_INTROFFSET); ++ struct icside_state *state = ec->irq_data; ++ unsigned int base = state->irq_port; ++ ++ inb(base + ICS_ARCIN_V5_INTROFFSET); + } + + static const expansioncard_ops_t icside_ops_arcin_v5 = { +- icside_irqenable_arcin_v5, +- icside_irqdisable_arcin_v5, +- NULL, +- NULL, +- NULL, +- NULL ++ .irqenable = icside_irqenable_arcin_v5, ++ .irqdisable = icside_irqdisable_arcin_v5, + }; + + +@@ -133,10 +118,21 @@ + */ + static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) + { +- unsigned int ide_base_port = (unsigned int)ec->irq_data; ++ struct icside_state *state = ec->irq_data; ++ unsigned int base = state->irq_port; ++ ++ state->enabled = 1; + +- outb(0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); +- outb(0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); ++ switch (state->channel) { ++ case 0: ++ outb(0, base + ICS_ARCIN_V6_INTROFFSET_1); ++ inb(base + ICS_ARCIN_V6_INTROFFSET_2); ++ break; ++ case 1: ++ outb(0, base + ICS_ARCIN_V6_INTROFFSET_2); ++ inb(base + ICS_ARCIN_V6_INTROFFSET_1); ++ break; ++ } + } + + /* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) +@@ -144,10 +140,12 @@ + */ + static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) + { +- unsigned int ide_base_port = (unsigned int)ec->irq_data; ++ struct icside_state *state = ec->irq_data; ++ ++ state->enabled = 0; + +- inb(ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); +- inb(ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); ++ inb (state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); ++ inb (state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + } + + /* Prototype: icside_irqprobe(struct expansion_card *ec) +@@ -155,70 +153,49 @@ + */ + static int icside_irqpending_arcin_v6(struct expansion_card *ec) + { +- unsigned int ide_base_port = (unsigned int)ec->irq_data; ++ struct icside_state *state = ec->irq_data; + +- return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 || +- inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1; ++ return inb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 || ++ inb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_2) & 1; + } + + static const expansioncard_ops_t icside_ops_arcin_v6 = { +- icside_irqenable_arcin_v6, +- icside_irqdisable_arcin_v6, +- icside_irqpending_arcin_v6, +- NULL, +- NULL, +- NULL ++ .irqenable = icside_irqenable_arcin_v6, ++ .irqdisable = icside_irqdisable_arcin_v6, ++ .irqpending = icside_irqpending_arcin_v6, + }; + +-/* Prototype: icside_identifyif (struct expansion_card *ec) +- * Purpose : identify IDE interface type +- * Notes : checks the description string ++/* ++ * Handle routing of interrupts. This is called before ++ * we write the command to the drive. + */ +-static iftype_t __init icside_identifyif (struct expansion_card *ec) ++static void icside_maskproc(ide_drive_t *drive, int mask) + { +- unsigned int addr; +- iftype_t iftype; +- int id = 0; +- +- iftype = ics_if_unknown; +- +- addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET; +- +- id = inb(addr) & 1; +- id |= (inb(addr + 1) & 1) << 1; +- id |= (inb(addr + 2) & 1) << 2; +- id |= (inb(addr + 3) & 1) << 3; +- +- switch (id) { +- case 0: /* A3IN */ +- printk("icside: A3IN unsupported\n"); +- break; +- +- case 1: /* A3USER */ +- printk("icside: A3USER unsupported\n"); +- break; ++ ide_hwif_t *hwif = HWIF(drive); ++ struct icside_state *state = hwif->hwif_data; ++ unsigned long flags; + +- case 3: /* ARCIN V6 */ +- printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no); +- iftype = ics_if_arcin_v6; +- break; ++ local_irq_save(flags); + +- case 15:/* ARCIN V5 (no id) */ +- printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no); +- iftype = ics_if_arcin_v5; +- break; ++ state->channel = hwif->channel; + +- default:/* we don't know - complain very loudly */ +- printk("icside: ***********************************\n"); +- printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id); +- printk("icside: ***********************************\n"); +- printk("icside: please report this to linux@arm.linux.org.uk\n"); +- printk("icside: defaulting to ARCIN V5\n"); +- iftype = ics_if_arcin_v5; +- break; ++ if (state->enabled && !mask) { ++ switch (hwif->channel) { ++ case 0: ++ outb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); ++ inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); ++ break; ++ case 1: ++ outb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); ++ inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); ++ break; ++ } ++ } else { ++ inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); ++ inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + } + +- return iftype; ++ local_irq_restore(flags); + } + + #ifdef CONFIG_BLK_DEV_IDEDMA_ICS +@@ -234,125 +211,138 @@ + #define NR_ENTRIES 256 + #define TABLE_SIZE (NR_ENTRIES * 8) + +-static int ide_build_sglist(ide_hwif_t *hwif, struct request *rq) ++static void ide_build_sglist(ide_drive_t *drive, struct request *rq) + { +- struct buffer_head *bh; ++ ide_hwif_t *hwif = HWIF(drive); + struct scatterlist *sg = hwif->sg_table; ++ struct buffer_head *bh; + int nents = 0; + +- if (rq->cmd == READ) +- hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; +- else +- hwif->sg_dma_direction = PCI_DMA_TODEVICE; +- bh = rq->bh; +- do { +- unsigned char *virt_addr = bh->b_data; +- unsigned int size = bh->b_size; +- +- while ((bh = bh->b_reqnext) != NULL) { +- if ((virt_addr + size) != (unsigned char *)bh->b_data) +- break; +- size += bh->b_size; +- } +- memset(&sg[nents], 0, sizeof(*sg)); +- sg[nents].address = virt_addr; +- sg[nents].length = size; +- nents++; +- } while (bh != NULL); ++ BUG_ON(hwif->sg_dma_active); + +- return pci_map_sg(NULL, sg, nents, hwif->sg_dma_direction); +-} ++ if (rq->cmd == IDE_DRIVE_TASKFILE) { ++ ide_task_t *args = rq->special; + +-static int +-icside_build_dmatable(ide_drive_t *drive, int ddir) +-{ +- return HWIF(drive)->sg_nents = ide_build_sglist(HWIF(drive), HWGROUP(drive)->rq, ddir); +-} ++ if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE) ++ hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; ++ else ++ hwif->sg_dma_direction = PCI_DMA_TODEVICE; ++ ++ memset(sg, 0, sizeof(*sg)); ++ sg->address = rq->buffer; ++ sg->length = rq->nr_sectors * SECTOR_SIZE; ++ nents = 1; ++ } else { ++ if (rq->cmd == READ) ++ hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; ++ else ++ hwif->sg_dma_direction = PCI_DMA_TODEVICE; ++ ++ bh = rq->bh; ++ do { ++ unsigned long lastend; ++ ++ memset(sg, 0, sizeof(*sg)); ++ sg->page = bh->b_page; ++ lastend = bh_phys(bh); ++ ++ do { ++ lastend += bh->b_size; ++ sg->length += bh->b_size; ++ ++ bh = bh->b_reqnext; ++ if (bh == NULL) ++ break; ++ } while (lastend == bh_phys(bh)); ++ ++ sg++; ++ nents++; ++ } while (bh != NULL); ++ } + +-/* Teardown mappings after DMA has completed. */ +-static void icside_destroy_dmatable(ide_drive_t *drive) +-{ +- struct scatterlist *sg = HWIF(drive)->sg_table; +- int nents = HWIF(drive)->sg_nents; ++ nents = pci_map_sg(NULL, sg, nents, hwif->sg_dma_direction); + +- pci_unmap_sg(NULL, sg, nents, HWIF(drive)->sg_dma_direction); ++ hwif->sg_nents = nents; + } + +-static int +-icside_config_if(ide_drive_t *drive, int xfer_mode) ++ ++/* ++ * Configure the IOMD to give the appropriate timings for the transfer ++ * mode being requested. We take the advice of the ATA standards, and ++ * calculate the cycle time based on the transfer mode, and the EIDE ++ * MW DMA specs that the drive provides in the IDENTIFY command. ++ * ++ * We have the following IOMD DMA modes to choose from: ++ * ++ * Type Active Recovery Cycle ++ * A 250 (250) 312 (550) 562 (800) ++ * B 187 250 437 ++ * C 125 (125) 125 (375) 250 (500) ++ * D 62 125 187 ++ * ++ * (figures in brackets are actual measured timings) ++ * ++ * However, we also need to take care of the read/write active and ++ * recovery timings: ++ * ++ * Read Write ++ * Mode Active -- Recovery -- Cycle IOMD type ++ * MW0 215 50 215 480 A ++ * MW1 80 50 50 150 C ++ * MW2 70 25 25 120 C ++ */ ++static int icside_set_speed(ide_drive_t *drive, u8 xfer_mode) + { +- int func = ide_dma_off; ++ int on = 0, cycle_time = 0, use_dma_info = 0; ++ ++ /* ++ * Limit the transfer speed to MW_DMA_2. ++ */ ++ if (xfer_mode > XFER_MW_DMA_2) ++ xfer_mode = XFER_MW_DMA_2; + + switch (xfer_mode) { + case XFER_MW_DMA_2: +- /* +- * The cycle time is limited to 250ns by the r/w +- * pulse width (90ns), however we should still +- * have a maximum burst transfer rate of 8MB/s. +- */ +- drive->drive_data = 250; ++ cycle_time = 250; ++ use_dma_info = 1; + break; + + case XFER_MW_DMA_1: +- drive->drive_data = 250; ++ cycle_time = 250; ++ use_dma_info = 1; + break; + + case XFER_MW_DMA_0: +- drive->drive_data = 480; ++ cycle_time = 480; + break; + +- default: +- drive->drive_data = 0; ++ case XFER_SW_DMA_2: ++ case XFER_SW_DMA_1: ++ case XFER_SW_DMA_0: ++ cycle_time = 480; + break; + } + +- if (!drive->init_speed) +- drive->init_speed = (u8) xfer_mode; ++ /* ++ * If we're going to be doing MW_DMA_1 or MW_DMA_2, we should ++ * take care to note the values in the ID... ++ */ ++ if (use_dma_info && drive->id->eide_dma_time > cycle_time) ++ cycle_time = drive->id->eide_dma_time; ++ ++ drive->drive_data = cycle_time; + +- if (drive->drive_data && +- ide_config_drive_speed(drive, (u8) xfer_mode) == 0) +- func = ide_dma_on; ++ if (cycle_time && ide_config_drive_speed(drive, xfer_mode) == 0) ++ on = 1; + else + drive->drive_data = 480; + + printk("%s: %s selected (peak %dMB/s)\n", drive->name, + ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data); + +- drive->current_speed = (u8) xfer_mode; +- +- return func; +-} +- +-static int +-icside_set_speed(ide_drive_t *drive, u8 speed) +-{ +- return icside_config_if(drive, speed); +-} +- +-/* +- * dma_intr() is the handler for disk read/write DMA interrupts +- */ +-static ide_startstop_t icside_dmaintr(ide_drive_t *drive) +-{ +- u8 dma_stat = HWIF(drive)->ide_dma_end(drive); +- /* get drive status */ +- u8 stat = HWIF(drive)->INB(IDE_STATUS_REG); +- int i; ++ drive->current_speed = xfer_mode; + +- if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { +- if (!dma_stat) { +- struct request *rq = HWGROUP(drive)->rq; +- rq = HWGROUP(drive)->rq; +- for (i = rq->nr_sectors; i > 0;) { +- i -= rq->current_nr_sectors; +- DRIVER(drive)->end_request(drive, 1); +- } +- return ide_stopped; +- } +- printk("%s: dma_intr: bad DMA status (dma_stat=%x)\n", +- drive->name, dma_stat); +- } +- return DRIVER(drive)->error(drive, "dma_intr", stat); ++ return on; + } + + /* +@@ -361,19 +351,19 @@ + * This should be defined in one place only. + */ + struct drive_list_entry { +- char * id_model; +- char * id_firmware; ++ const char * id_model; ++ const char * id_firmware; + }; + +-static struct drive_list_entry drive_whitelist [] = { ++static const struct drive_list_entry drive_whitelist [] = { + { "Micropolis 2112A", "ALL" }, + { "CONNER CTMA 4000", "ALL" }, + { "CONNER CTT8000-A", "ALL" }, + { "ST34342A", "ALL" }, +- { NULL, 0 } ++ { NULL, NULL } + }; + +-static struct drive_list_entry drive_blacklist [] = { ++static const struct drive_list_entry drive_blacklist [] = { + { "WDC AC11000H", "ALL" }, + { "WDC AC22100H", "ALL" }, + { "WDC AC32500H", "ALL" }, +@@ -407,10 +397,11 @@ + { "PLEXTOR CD-R PX-W8432T", "ALL" }, + { "ATAPI CD-ROM DRIVE 40X MAXIMUM", "ALL" }, + { "_NEC DV5800A", "ALL" }, +- { NULL, 0 } ++ { NULL, NULL } + }; + +-static int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table) ++static int ++in_drive_list(struct hd_driveid *id, const struct drive_list_entry *drive_table) + { + for ( ; drive_table->id_model ; drive_table++) + if ((!strcmp(drive_table->id_model, id->model)) && +@@ -420,41 +411,52 @@ + return 0; + } + +-/* +- * For both Blacklisted and Whitelisted drives. +- * This is setup to be called as an extern for future support +- * to other special driver code. +- */ +-int check_drive_good_lists (ide_drive_t *drive) ++static int icside_dma_host_off(ide_drive_t *drive) + { +- struct hd_driveid *id = drive->id; +- return in_drive_list(id, drive_whitelist); ++ return 0; + } + +-int check_drive_bad_lists (ide_drive_t *drive) ++static int icside_dma_off_quietly(ide_drive_t *drive) + { +- struct hd_driveid *id = drive->id; +- int blacklist = in_drive_list(id, drive_blacklist); +- if (blacklist) +- printk("%s: Disabling DMA for %s\n", drive->name, id->model); +- return(blacklist); ++ drive->using_dma = 0; ++ return icside_dma_host_off(drive); ++} ++ ++static int icside_dma_off(ide_drive_t *drive) ++{ ++ printk("%s: DMA disabled\n", drive->name); ++ return icside_dma_off_quietly(drive); ++} ++ ++static int icside_dma_host_on(ide_drive_t *drive) ++{ ++ return 0; + } + +-int icside_dma_check(ide_drive_t *drive) ++static int icside_dma_on(ide_drive_t *drive) ++{ ++ drive->using_dma = 1; ++ return icside_dma_host_on(drive); ++} ++ ++static int icside_dma_check(ide_drive_t *drive) + { + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); +- int autodma = hwif->autodma; + int xfer_mode = XFER_PIO_2; ++ int on; + +- if (!id || !(id->capability & 1) || !autodma) +- return hwif->ide_dma_off_quietly(drive); ++ if (!id || !(id->capability & 1) || !hwif->autodma) ++ goto out; + + /* + * Consult the list of known "bad" drives + */ +- if (check_drive_bad_lists(drive)) +- return hwif->ide_dma_off(drive); ++ if (in_drive_list(id, drive_blacklist)) { ++ printk("%s: Disabling DMA for %s (blacklisted)\n", ++ drive->name, id->model); ++ goto out; ++ } + + /* + * Enable DMA on any drive that has multiword DMA +@@ -473,192 +475,241 @@ + /* + * Consult the list of known "good" drives + */ +- if (check_drive_good_lists(drive)) { ++ if (in_drive_list(id, drive_whitelist)) { + if (id->eide_dma_time > 150) + goto out; + xfer_mode = XFER_MW_DMA_1; + } + + out: +- if (icside_config_if(drive, xfer_mode)) +- return hwif->ide_dma_on(drive); +- return hwif->ide_dma_off(drive); +-} ++ on = icside_set_speed(drive, xfer_mode); + +-int icside_dma_verbose(ide_drive_t *drive) +-{ +- printk(", DMA"); +- return 1; ++ if (on) ++ return icside_dma_on(drive); ++ else ++ return icside_dma_off(drive); + } + +-int icside_dma_test_irq(ide_drive_t *drive) ++static int icside_dma_end(ide_drive_t *drive) + { + ide_hwif_t *hwif = HWIF(drive); +- return inb((unsigned long)hwif->hw.priv) & 1; +-} + +-int icside_dma_host_off(ide_drive_t *drive) +-{ +- return 0; +-} ++ drive->waiting_for_dma = 0; + +-int icside_dma_off_quietly(ide_drive_t *drive) +-{ +- drive->using_dma = 0; +- return icside_dma_host_off(drive); +-} ++ disable_dma(hwif->hw.dma); + +-int icside_dma_off(ide_drive_t *drive) +-{ +- printk("%s: DMA disabled\n", drive->name); +- return icside_dma_off_quietly(drive); +-} ++ /* Teardown mappings after DMA has completed. */ ++ pci_unmap_sg(NULL, hwif->sg_table, hwif->sg_nents, ++ hwif->sg_dma_direction); + +-int icside_dma_host_on(ide_drive_t *drive) +-{ +- return 0; +-} ++ hwif->sg_dma_active = 0; + +-int icside_dma_on(ide_drive_t *drive) +-{ +- drive->using_dma = 1; +- return icside_dma_host_on(drive); ++ return get_dma_residue(hwif->hw.dma) != 0; + } + +-int icside_dma_begin(ide_drive_t *drive) ++static int icside_dma_begin(ide_drive_t *drive) + { + ide_hwif_t *hwif = HWIF(drive); + ++ /* We can not enable DMA on both channels simultaneously. */ ++ BUG_ON(dma_channel_active(hwif->hw.dma)); + enable_dma(hwif->hw.dma); + return 0; + } + +-int icside_dma_end(ide_drive_t *drive) ++static int icside_dma_count(ide_drive_t *drive) + { +- ide_hwif_t *hwif = HWIF(drive); +- +- drive->waiting_for_dma = 0; +- disable_dma(hwif->hw.dma); +- icside_destroy_dmatable(drive); +- return get_dma_residue(hwif->hw.dma) != 0; ++ return icside_dma_begin(drive); + } + +-int icside_dma_count (ide_drive_t *drive) ++/* ++ * dma_intr() is the handler for disk read/write DMA interrupts ++ */ ++static ide_startstop_t icside_dmaintr(ide_drive_t *drive) + { +- return icside_dma_begin(drive); ++ unsigned int stat; ++ int dma_stat; ++ ++ dma_stat = icside_dma_end(drive); ++ stat = HWIF(drive)->INB(IDE_STATUS_REG); ++ if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | DRQ_STAT)) { ++ if (!dma_stat) { ++ struct request *rq = HWGROUP(drive)->rq; ++ int i; ++ ++ for (i = rq->nr_sectors; i > 0; ) { ++ i -= rq->current_nr_sectors; ++ DRIVER(drive)->end_request(drive, 1); ++ } ++ ++ return ide_stopped; ++ } ++ printk(KERN_ERR "%s: bad DMA status (dma_stat=%x)\n", ++ drive->name, dma_stat); ++ } ++ ++ return DRIVER(drive)->error(drive, __FUNCTION__, stat); + } + +-int icside_dma_read(ide_drive_t *drive) ++static int ++icside_dma_common(ide_drive_t *drive, struct request *rq, ++ unsigned int dma_mode) + { +- ide_hwif_t *hwif = HWIF(drive); +-// ide_task_t *args = HWGROUP(drive)->rq->special; +- int count = 0; +- u8 lba48 = (drive->addressing == 1) ? 1 : 0; +- task_ioreg_t command = WIN_NOP; ++ ide_hwif_t *hwif = HWIF(drive); + +- count = icside_build_dmatable(drive, PCI_DMA_FROMDEVICE); +- if (!count) +- return 1; +- disable_dma(hwif->hw.dma); ++ /* ++ * We can not enable DMA on both channels. ++ */ ++ BUG_ON(hwif->sg_dma_active); ++ BUG_ON(dma_channel_active(hwif->hw.dma)); ++ ++ ide_build_sglist(drive, rq); + +- /* Route the DMA signals to +- * to the correct interface. ++ /* ++ * Ensure that we have the right interrupt routed. + */ +- HWIF(drive)->OUTB(hwif->select_data, hwif->config_data); ++ icside_maskproc(drive, 0); + +- /* Select the correct timing +- * for this drive ++ /* ++ * Route the DMA signals to the correct interface. ++ */ ++ outb(hwif->select_data, hwif->config_data); ++ ++ /* ++ * Select the correct timing for this drive. + */ + set_dma_speed(hwif->hw.dma, drive->drive_data); + +- set_dma_sg(hwif->hw.dma, HWIF(drive)->sg_table, count); +- set_dma_mode(hwif->hw.dma, DMA_MODE_READ); ++ /* ++ * Tell the DMA engine about the SG table and ++ * data direction. ++ */ ++ set_dma_sg(hwif->hw.dma, hwif->sg_table, hwif->sg_nents); ++ set_dma_mode(hwif->hw.dma, dma_mode); ++ ++ return 0; ++} ++ ++static int icside_dma_read(ide_drive_t *drive) ++{ ++ struct request *rq = HWGROUP(drive)->rq; ++ task_ioreg_t cmd; ++ ++ if (icside_dma_common(drive, rq, DMA_MODE_READ)) ++ return 1; + + drive->waiting_for_dma = 1; ++ + if (drive->media != ide_disk) + return 0; + +- if (HWGROUP(drive)->handler != NULL) /* paranoia check */ +- BUG(); +- ide_set_handler(drive, &icside_dmaintr, WAIT_CMD, NULL); + /* + * FIX ME to use only ACB ide_task_t args Struct + */ + #if 0 + { +- ide_task_t *args = HWGROUP(drive)->rq->special; +- command = args->tfRegister[IDE_COMMAND_OFFSET]; ++ ide_task_t *args = rq->special; ++ cmd = args->tfRegister[IDE_COMMAND_OFFSET]; + } + #else +- command = (lba48) ? WIN_READDMA_EXT : WIN_READDMA; +- if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE) { +- ide_task_t *args = HWGROUP(drive)->rq->special; +- command = args->tfRegister[IDE_COMMAND_OFFSET]; ++ if (rq->cmd == IDE_DRIVE_TASKFILE) { ++ ide_task_t *args = rq->special; ++ cmd = args->tfRegister[IDE_COMMAND_OFFSET]; ++ } else if (drive->addressing == 1) { ++ cmd = WIN_READDMA_EXT; ++ } else { ++ cmd = WIN_READDMA; + } + #endif +- /* issue cmd to drive */ +- HWIF(drive)->OUTB(command, IDE_COMMAND_REG); + +- return icside_dma_count(drive); ++ ide_execute_command(drive, cmd, icside_dmaintr, 2*WAIT_CMD, NULL); ++ ++ return icside_dma_begin(drive); + } + +-int icside_dma_write(ide_drive_t *drive) ++static int icside_dma_write(ide_drive_t *drive) + { +- ide_hwif_t *hwif = HWIF(drive); +-// ide_task_t *args = HWGROUP(drive)->rq->special; +- int count = 0; +- u8 lba48 = (drive->addressing == 1) ? 1 : 0; +- task_ioreg_t command = WIN_NOP; ++ struct request *rq = HWGROUP(drive)->rq; ++ task_ioreg_t cmd; + +- count = icside_build_dmatable(drive, PCI_DMA_TODEVICE); +- if (!count) ++ if (icside_dma_common(drive, rq, DMA_MODE_WRITE)) + return 1; +- disable_dma(hwif->hw.dma); +- +- /* Route the DMA signals to +- * to the correct interface. +- */ +- HWIF(drive)->OUTB(hwif->select_data, hwif->config_data); +- +- /* Select the correct timing +- * for this drive +- */ +- set_dma_speed(hwif->hw.dma, drive->drive_data); +- +- set_dma_sg(hwif->hw.dma, HWIF(drive)->sg_table, count); +- set_dma_mode(hwif->hw.dma, DMA_MODE_WRITE); + + drive->waiting_for_dma = 1; ++ + if (drive->media != ide_disk) + return 0; + +- if (HWGROUP(drive)->handler != NULL) +- BUG(); +- ide_set_handler(drive, &icside_dmaintr, WAIT_CMD, NULL); + /* + * FIX ME to use only ACB ide_task_t args Struct + */ + #if 0 + { +- ide_task_t *args = HWGROUP(drive)->rq->special; +- command = args->tfRegister[IDE_COMMAND_OFFSET]; ++ ide_task_t *args = rq->special; ++ cmd = args->tfRegister[IDE_COMMAND_OFFSET]; + } + #else +- command = (lba48) ? WIN_WRITEDMA_EXT : WIN_WRITEDMA; +- if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE) { +- ide_task_t *args = HWGROUP(drive)->rq->special; +- command = args->tfRegister[IDE_COMMAND_OFFSET]; ++ if (rq->cmd == IDE_DRIVE_TASKFILE) { ++ ide_task_t *args = rq->special; ++ cmd = args->tfRegister[IDE_COMMAND_OFFSET]; ++ } else if (drive->addressing == 1) { ++ cmd = WIN_WRITEDMA_EXT; ++ } else { ++ cmd = WIN_WRITEDMA; + } + #endif +- /* issue cmd to drive */ +- HWIF(drive)->OUTB(command, IDE_COMMAND_REG); + +- return icside_dma_count(drive); ++ ide_execute_command(drive, cmd, icside_dmaintr, 2*WAIT_CMD, NULL); ++ ++ return icside_dma_begin(drive); + } + +-static int +-icside_setup_dma(ide_hwif_t *hwif, int autodma) ++static int icside_dma_test_irq(ide_drive_t *drive) ++{ ++ ide_hwif_t *hwif = HWIF(drive); ++ struct icside_state *state = hwif->hwif_data; ++ ++ return inb(state->irq_port + ++ (hwif->channel ? ++ ICS_ARCIN_V6_INTRSTAT_2 : ++ ICS_ARCIN_V6_INTRSTAT_1)) & 1; ++} ++ ++static int icside_dma_verbose(ide_drive_t *drive) ++{ ++ printk(", %s (peak %dMB/s)", ++ ide_xfer_verbose(drive->current_speed), ++ 2000 / drive->drive_data); ++ return 1; ++} ++ ++static int icside_dma_timeout(ide_drive_t *drive) ++{ ++ printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name); ++ ++ if (icside_dma_test_irq(drive)) ++ return 0; ++ ++ ide_dump_status(drive, "DMA timeout", ++ HWIF(drive)->INB(IDE_STATUS_REG)); ++ ++ return icside_dma_end(drive); ++} ++ ++static int icside_dma_lostirq(ide_drive_t *drive) ++{ ++ printk(KERN_ERR "%s: IRQ lost\n", drive->name); ++ return 1; ++} ++ ++static int icside_dma_init(ide_hwif_t *hwif) + { ++ int autodma = 0; ++ ++#ifdef CONFIG_IDEDMA_ICS_AUTO ++ autodma = 1; ++#endif ++ + printk(" %s: SG-DMA", hwif->name); + + hwif->sg_table = kmalloc(sizeof(struct scatterlist) * NR_ENTRIES, +@@ -666,40 +717,53 @@ + if (!hwif->sg_table) + goto failed; + +- hwif->dmatable_cpu = NULL; +- hwif->dmatable_dma = 0; +- hwif->speedproc = icside_set_speed; +- hwif->autodma = autodma; ++ hwif->atapi_dma = 1; ++ hwif->mwdma_mask = 7; /* MW0..2 */ ++ hwif->swdma_mask = 7; /* SW0..2 */ ++ ++ hwif->dmatable_cpu = NULL; ++ hwif->dmatable_dma = 0; ++ hwif->speedproc = icside_set_speed; ++ hwif->autodma = autodma; + +- hwif->ide_dma_check = icside_dma_check; +- hwif->ide_dma_host_off = icside_dma_host_off; ++ hwif->ide_dma_check = icside_dma_check; ++ hwif->ide_dma_host_off = icside_dma_host_off; + hwif->ide_dma_off_quietly = icside_dma_off_quietly; +- hwif->ide_dma_off = icside_dma_off; +- hwif->ide_dma_host_on = icside_dma_host_on; +- hwif->ide_dma_on = icside_dma_on; +- hwif->ide_dma_read = icside_dma_read; +- hwif->ide_dma_write = icside_dma_write; +- hwif->ide_dma_count = icside_dma_count; +- hwif->ide_dma_begin = icside_dma_begin; +- hwif->ide_dma_end = icside_dma_end; +- hwif->ide_dma_verbose = icside_dma_verbose; +- hwif->ide_dma_bad_drive = check_drive_bad_lists; +- hwif->ide_dma_good_drive = check_drive_good_lists; +- hwif->ide_dma_test_irq = icside_dma_test_irq; ++ hwif->ide_dma_off = icside_dma_off; ++ hwif->ide_dma_host_on = icside_dma_host_on; ++ hwif->ide_dma_on = icside_dma_on; ++ hwif->ide_dma_read = icside_dma_read; ++ hwif->ide_dma_write = icside_dma_write; ++ hwif->ide_dma_count = icside_dma_count; ++ hwif->ide_dma_begin = icside_dma_begin; ++ hwif->ide_dma_end = icside_dma_end; ++ hwif->ide_dma_test_irq = icside_dma_test_irq; ++ hwif->ide_dma_verbose = icside_dma_verbose; ++ hwif->ide_dma_timeout = icside_dma_timeout; ++ hwif->ide_dma_lostirq = icside_dma_lostirq; + +- printk(" capable%s\n", autodma ? +- ", auto-enable" : ""); ++ printk(" capable%s\n", hwif->autodma ? ", auto-enable" : ""); + + return 1; + + failed: +- printk(" -- ERROR, unable to allocate DMA table\n"); ++ printk(" disabled, unable to allocate DMA table\n"); + return 0; + } ++ ++static void icside_dma_exit(ide_hwif_t *hwif) ++{ ++ if (hwif->sg_table) { ++ kfree(hwif->sg_table); ++ hwif->sg_table = NULL; ++ } ++} ++#else ++#define icside_dma_init(hwif) (0) ++#define icside_dma_exit(hwif) do { } while (0) + #endif + +-static ide_hwif_t * +-icside_find_hwif(unsigned long dataport) ++static ide_hwif_t *icside_find_hwif(unsigned long dataport) + { + ide_hwif_t *hwif; + int index; +@@ -716,13 +780,13 @@ + goto found; + } + +- return NULL; ++ hwif = NULL; + found: + return hwif; + } + + static ide_hwif_t * +-icside_setup(unsigned long base, struct cardinfo *info, int irq) ++icside_setup(unsigned long base, struct cardinfo *info, struct expansion_card *ec) + { + unsigned long port = base + info->dataoffset; + ide_hwif_t *hwif; +@@ -740,8 +804,8 @@ + } + hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset; + hwif->io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset; +- hwif->hw.irq = irq; +- hwif->irq = irq; ++ hwif->hw.irq = ec->irq; ++ hwif->irq = ec->irq; + hwif->hw.dma = NO_DMA; + hwif->noprobe = 0; + hwif->chipset = ide_acorn; +@@ -750,33 +814,39 @@ + return hwif; + } + +-static int __init icside_register_v5(struct expansion_card *ec, int autodma) ++static int __init ++icside_register_v5(struct icside_state *state, struct expansion_card *ec) + { + unsigned long slot_port; + ide_hwif_t *hwif; + + slot_port = ecard_address(ec, ECARD_MEMC, 0); + ++ state->irq_port = slot_port; ++ + ec->irqaddr = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT); + ec->irqmask = 1; +- ec->irq_data = (void *)slot_port; +- ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5; ++ ec->irq_data = state; ++ ec->ops = &icside_ops_arcin_v5; + + /* + * Be on the safe side - disable interrupts + */ + inb(slot_port + ICS_ARCIN_V5_INTROFFSET); + +- hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq); ++ hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec); + +- return hwif ? 0 : -1; ++ state->hwif[0] = hwif; ++ ++ return hwif ? 0 : -ENODEV; + } + +-static int __init icside_register_v6(struct expansion_card *ec, int autodma) ++static int __init ++icside_register_v6(struct icside_state *state, struct expansion_card *ec) + { + unsigned long slot_port, port; + ide_hwif_t *hwif, *mate; +- int sel = 0; ++ unsigned int sel = 0; + + slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST); + port = ecard_address(ec, ECARD_EASI, ECARD_FAST); +@@ -788,88 +858,185 @@ + + outb(sel, slot_port); + +- ec->irq_data = (void *)port; +- ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6; +- + /* + * Be on the safe side - disable interrupts + */ + inb(port + ICS_ARCIN_V6_INTROFFSET_1); + inb(port + ICS_ARCIN_V6_INTROFFSET_2); + +- hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq); +- mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq); ++ /* ++ * Find and register the interfaces. ++ */ ++ hwif = icside_setup(port, &icside_cardinfo_v6_1, ec); ++ mate = icside_setup(port, &icside_cardinfo_v6_2, ec); ++ ++ if (!hwif || !mate) ++ return -ENODEV; + +-#ifdef CONFIG_BLK_DEV_IDEDMA_ICS +- if (ec->dma != NO_DMA) { +- if (request_dma(ec->dma, hwif->name)) +- goto no_dma; +- +- if (hwif) { +- hwif->config_data = slot_port; +- hwif->select_data = sel; +- hwif->hw.dma = ec->dma; +- hwif->hw.priv = (void *) +- (port + ICS_ARCIN_V6_INTRSTAT_1); +- hwif->channel = 0; +- icside_setup_dma(hwif, autodma); +- hwif->drives[0].autodma = autodma; +- hwif->drives[1].autodma = autodma; +- } +- if (mate) { +- mate->config_data = slot_port; +- mate->select_data = sel | 1; +- mate->hw.dma = ec->dma; +- mate->hw.priv = (void *) +- (port + ICS_ARCIN_V6_INTRSTAT_2); +- mate->channel = 1; +- icside_setup_dma(mate, autodma); +- mate->drives[0].autodma = autodma; +- mate->drives[1].autodma = autodma; +- } ++ state->irq_port = port; ++ state->slot_port = slot_port; ++ state->hwif[0] = hwif; ++ state->hwif[1] = mate; ++ ++ ec->irq_data = state; ++ ec->ops = &icside_ops_arcin_v6; ++ ++ hwif->maskproc = icside_maskproc; ++ hwif->channel = 0; ++ hwif->hwif_data = state; ++ hwif->mate = mate; ++ hwif->serialized = 1; ++ hwif->config_data = slot_port; ++ hwif->select_data = sel; ++ hwif->hw.dma = ec->dma; ++ ++ mate->maskproc = icside_maskproc; ++ mate->channel = 1; ++ mate->hwif_data = state; ++ mate->mate = hwif; ++ mate->serialized = 1; ++ mate->config_data = slot_port; ++ mate->select_data = sel | 1; ++ mate->hw.dma = ec->dma; ++ ++ if (ec->dma != NO_DMA && !request_dma(ec->dma, hwif->name)) { ++ icside_dma_init(hwif); ++ icside_dma_init(mate); + } +-no_dma: +-#endif +- return hwif || mate ? 0 : -1; ++ return 0; + } + +-int __init icside_init(void) ++static int __init icside_probe(struct expansion_card *ec, const struct ecard_id *id) + { +- int autodma = 0; ++ struct icside_state *state; ++ int ret; + +-#ifdef CONFIG_IDEDMA_ICS_AUTO +- autodma = 1; +-#endif ++ state = kmalloc(sizeof(struct icside_state), GFP_KERNEL); ++ if (!state) { ++ ret = -ENOMEM; ++ goto out; ++ } + +- ecard_startfind (); ++ memset(state, 0, sizeof(struct icside_state)); ++ state->type = ICS_TYPE_NOTYPE; + +- do { +- struct expansion_card *ec; +- int result; ++ { ++ unsigned int addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET; ++ unsigned int type; + +- ec = ecard_find(0, icside_cids); +- if (ec == NULL) +- break; ++ type = inb(addr) & 1; ++ type |= (inb(addr + 1) & 1) << 1; ++ type |= (inb(addr + 2) & 1) << 2; ++ type |= (inb(addr + 3) & 1) << 3; + +- ecard_claim(ec); ++ state->type = type; ++ } + +- switch (icside_identifyif(ec)) { +- case ics_if_arcin_v5: +- result = icside_register_v5(ec, autodma); +- break; ++ switch (state->type) { ++ case ICS_TYPE_A3IN: ++ printk(KERN_WARNING "icside: A3IN unsupported\n"); ++ ret = -ENODEV; ++ break; + +- case ics_if_arcin_v6: +- result = icside_register_v6(ec, autodma); +- break; ++ case ICS_TYPE_A3USER: ++ printk(KERN_WARNING "icside: A3USER unsupported\n"); ++ ret = -ENODEV; ++ break; + +- default: +- result = -1; +- break; +- } ++ case ICS_TYPE_V5: ++ ret = icside_register_v5(state, ec); ++ break; + +- if (result) +- ecard_release(ec); +- } while (1); ++ case ICS_TYPE_V6: ++ ret = icside_register_v6(state, ec); ++ break; + +- return 0; ++ default: ++ printk(KERN_WARNING "icside: unknown interface type\n"); ++ ret = -ENODEV; ++ break; ++ } ++ ++ if (ret == 0) { ++ ecard_set_drvdata(ec, state); ++ } else { ++ kfree(state); ++ } ++ out: ++ return ret; ++} ++ ++static void __devexit icside_remove(struct expansion_card *ec) ++{ ++ struct icside_state *state = ecard_get_drvdata(ec); ++ ++ switch (state->type) { ++ case ICS_TYPE_V5: ++ /* FIXME: tell IDE to stop using the interface */ ++ ++ /* Disable interrupts */ ++ inb(state->slot_port + ICS_ARCIN_V5_INTROFFSET); ++ break; ++ ++ case ICS_TYPE_V6: ++ /* FIXME: tell IDE to stop using the interface */ ++ icside_dma_exit(state->hwif[1]); ++ icside_dma_exit(state->hwif[0]); ++ ++ if (ec->dma != NO_DMA) ++ free_dma(ec->dma); ++ ++ /* Disable interrupts */ ++ inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); ++ inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); ++ ++ /* Reset the ROM pointer/EASI selection */ ++ outb(0, state->slot_port); ++ break; ++ } ++ ++ ecard_set_drvdata(ec, NULL); ++ ec->ops = NULL; ++ ec->irq_data = NULL; ++ ++ kfree(state); ++} ++ ++static void icside_shutdown(struct expansion_card *ec) ++{ ++ struct icside_state *state = ecard_get_drvdata(ec); ++ ++ switch (state->type) { ++ case ICS_TYPE_V5: ++ /* Disable interrupts */ ++ inb(state->slot_port + ICS_ARCIN_V5_INTROFFSET); ++ break; ++ ++ case ICS_TYPE_V6: ++ /* Disable interrupts */ ++ inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); ++ inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); ++ ++ /* Reset the ROM pointer/EASI selection */ ++ outb(0, state->slot_port); ++ break; ++ } ++} ++ ++static const struct ecard_id icside_ids[] = { ++ { MANU_ICS, PROD_ICS_IDE }, ++ { MANU_ICS2, PROD_ICS2_IDE }, ++ { 0xffff, 0xffff } ++}; ++ ++static struct ecard_driver icside_driver = { ++ .probe = icside_probe, ++ .remove = __devexit_p(icside_remove), ++ .shutdown = icside_shutdown, ++ .id_table = icside_ids, ++}; ++ ++int __init icside_init(void) ++{ ++ return ecard_register_driver(&icside_driver); + } +diff -urN linux-2.4.26/drivers/ide/arm/rstation-ide.c linux-2.4.26-vrs1/drivers/ide/arm/rstation-ide.c +--- linux-2.4.26/drivers/ide/arm/rstation-ide.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ide/arm/rstation-ide.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,78 @@ ++/* ++ * linux/drivers/ide/rs-ide.c ++ * ++ * Copyright (c) 2002 Ben Dooks ++ * Copyright (c) 2002 Simtec Electronics ++ * ++ * Simple RiscStation IDE support ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#ifndef CONFIG_ARCH_RISCSTATION ++#error "compiling this code for non-riscstation hardware is dangerous!" ++#endif ++ ++#define DRV_PREFIX "ide-rs" ++ ++#define IRQ_PRI (40+3) ++#define IRQ_SEC (40+4) ++ ++#define PORT_BASE ((0x2b800 - 0x10000) >> 2) ++#define SEC_OFF (0x400 >> 2) ++ ++int __init rside_reg(unsigned long base, unsigned int irq); ++ ++int __init rside_init(void) ++{ ++ int iotcr; ++ ++ if (!machine_is_riscstation()) { ++ printk(DRV_PREFIX ": hardware is not a RiscStation!\n"); ++ return 0; ++ } ++ ++ /* select correct area cycle time */ ++ ++ iotcr = inb(IOMD_IOTCR); ++ outb((iotcr & ~3) | 1, IOMD_IOTCR); ++ ++ /* register h/w */ ++ ++ rside_reg(PORT_BASE, IRQ_PRI); ++ rside_reg(PORT_BASE + SEC_OFF, IRQ_SEC); ++ ++ return 0; ++} ++ ++ ++int __init rside_reg(unsigned long port, unsigned int irq) ++{ ++ unsigned long addr, i; ++ hw_regs_t hw; ++ ++ hw.irq = irq; ++ ++ addr = port; ++ ++ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { ++ hw.io_ports[i] = (ide_ioreg_t)addr; ++ addr += 0x40 >> 2; ++ } ++ ++ hw.io_ports[IDE_CONTROL_OFFSET] = port + ((0xb80 - 0x800) >> 2); ++ ++ printk(DRV_PREFIX ": registering channel at %08lx, %08lx, irq %d\n", ++ port, hw.io_ports[IDE_CONTROL_OFFSET], irq); ++ ++ return ide_register_hw(&hw, NULL); ++} +diff -urN linux-2.4.26/drivers/ide/ide-probe.c linux-2.4.26-vrs1/drivers/ide/ide-probe.c +--- linux-2.4.26/drivers/ide/ide-probe.c 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ide/ide-probe.c 2004-04-18 21:47:50.000000000 +0100 +@@ -1297,11 +1297,11 @@ + hwif->name, hwif->major); + return (hwif->present = 0); + } +- ++ + if (init_irq(hwif)) { + int i = hwif->irq; + /* +- * It failed to initialise. Find the default IRQ for ++ * It failed to initialise. Find the default IRQ for + * this port and try that. + */ + if (!(hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET]))) { +@@ -1319,7 +1319,7 @@ + printk("%s: probed IRQ %d failed, using default.\n", + hwif->name, hwif->irq); + } +- ++ + init_gendisk(hwif); + blk_dev[hwif->major].data = hwif; + blk_dev[hwif->major].queue = ide_get_queue; +diff -urN linux-2.4.26/drivers/ide/ide-proc.c linux-2.4.26-vrs1/drivers/ide/ide-proc.c +--- linux-2.4.26/drivers/ide/ide-proc.c 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ide/ide-proc.c 2004-04-18 21:47:50.000000000 +0100 +@@ -425,6 +425,7 @@ + case ide_cy82c693: name = "cy82c693"; break; + case ide_4drives: name = "4drives"; break; + case ide_pmac: name = "pmac"; break; ++ case ide_acorn: name = "acorn"; break; + default: name = "(unknown)"; break; + } + len = sprintf(page, "%s\n", name); +diff -urN linux-2.4.26/drivers/ide/ide.c linux-2.4.26-vrs1/drivers/ide/ide.c +--- linux-2.4.26/drivers/ide/ide.c 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/ide/ide.c 2004-02-23 13:36:30.000000000 +0000 +@@ -218,23 +218,14 @@ + static void init_hwif_data (unsigned int index) + { + unsigned int unit; +- hw_regs_t hw; + ide_hwif_t *hwif = &ide_hwifs[index]; + + /* bulk initialize hwif & drive info with zeros */ + memset(hwif, 0, sizeof(ide_hwif_t)); +- memset(&hw, 0, sizeof(hw_regs_t)); + + /* fill in any non-zero initial values */ + hwif->index = index; +- ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, &hwif->irq); +- memcpy(&hwif->hw, &hw, sizeof(hw)); +- memcpy(hwif->io_ports, hw.io_ports, sizeof(hw.io_ports)); +- hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; +-#ifdef CONFIG_BLK_DEV_HD +- if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA) +- hwif->noprobe = 1; /* may be overridden by ide_setup() */ +-#endif /* CONFIG_BLK_DEV_HD */ ++ hwif->noprobe = 1; + hwif->major = ide_hwif_to_major[index]; + hwif->name[0] = 'i'; + hwif->name[1] = 'd'; +@@ -276,6 +267,28 @@ + } + + /* ++ * Old compatability function - initialise ports using ide_default_io_base ++ */ ++static void ide_old_init_default_hwifs(void) ++{ ++ unsigned int index; ++ ide_ioreg_t base; ++ ide_hwif_t *hwif; ++ ++ for (index = 0; index < MAX_HWIFS; index++) { ++ hwif = &ide_hwifs[index]; ++ ++ base = ide_default_io_base(index); ++ ++ if (base) { ++ ide_init_hwif_ports(&hwif->hw, base, 0, &hwif->hw.irq); ++ memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports)); ++ hwif->noprobe = 0; ++ } ++ } ++} ++ ++/* + * init_ide_data() sets reasonable default values into all fields + * of all instances of the hwifs and drives, but only on the first call. + * Subsequent calls have no effect (they don't wipe out anything). +@@ -307,6 +320,7 @@ + init_hwif_data(index); + + /* Add default hw interfaces */ ++ ide_old_init_default_hwifs(); + ide_init_default_hwifs(); + + idebus_parameter = 0; +@@ -2530,6 +2544,12 @@ + rapide_init(); + } + #endif /* CONFIG_BLK_DEV_IDE_RAPIDE */ ++#ifdef CONFIG_BLK_DEV_IDE_RISCSTATION ++ { ++ extern void rside_init(void); ++ rside_init(); ++ } ++#endif /* CONFIG_BLK_DEV_IDE_RISCSTATION */ + #ifdef CONFIG_BLK_DEV_GAYLE + { + extern void gayle_init(void); +diff -urN linux-2.4.26/drivers/ide/pci/Makefile linux-2.4.26-vrs1/drivers/ide/pci/Makefile +--- linux-2.4.26/drivers/ide/pci/Makefile 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ide/pci/Makefile 2004-04-18 21:47:50.000000000 +0100 +@@ -16,7 +16,6 @@ + obj-$(CONFIG_BLK_DEV_HPT34X) += hpt34x.o + obj-$(CONFIG_BLK_DEV_HPT366) += hpt366.o + #obj-$(CONFIG_BLK_DEV_HPT37X) += hpt37x.o +-obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o + obj-$(CONFIG_BLK_DEV_IT8172) += it8172.o + obj-$(CONFIG_BLK_DEV_NS87415) += ns87415.o + obj-$(CONFIG_BLK_DEV_OPTI621) += opti621.o +diff -urN linux-2.4.26/drivers/ide/pci/sl82c105.c linux-2.4.26-vrs1/drivers/ide/pci/sl82c105.c +--- linux-2.4.26/drivers/ide/pci/sl82c105.c 2003-06-13 15:51:33.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ide/pci/sl82c105.c 2004-01-14 21:32:25.000000000 +0000 +@@ -37,7 +37,7 @@ + #ifdef DEBUG + #define DBG(arg) printk arg + #else +-#define DBG(fmt,...) ++#define DBG(fmt...) + #endif + /* + * SL82C105 PCI config register 0x40 bits. +diff -urN linux-2.4.26/drivers/ide/pci/sl82c105.c.2419 linux-2.4.26-vrs1/drivers/ide/pci/sl82c105.c.2419 +--- linux-2.4.26/drivers/ide/pci/sl82c105.c.2419 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ide/pci/sl82c105.c.2419 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,380 @@ ++/* ++ * linux/drivers/ide/sl82c105.c ++ * ++ * SL82C105/Winbond 553 IDE driver ++ * ++ * Maintainer unknown. ++ * ++ * Changelog: ++ * ++ * 15/11/1998 RMK Drive tuning added from Rebel.com's kernel ++ * sources ++ * 30/03/2002 RMK Add fixes specified in W83C553F errata. ++ * (with special thanks to Todd Inglett) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "ide_modes.h" ++ ++extern char *ide_xfer_verbose (byte xfer_rate); ++ ++/* ++ * SL82C105 PCI config register 0x40 bits. ++ */ ++#define CTRL_IDE_IRQB (1 << 30) ++#define CTRL_IDE_IRQA (1 << 28) ++#define CTRL_LEGIRQ (1 << 11) ++#define CTRL_P1F16 (1 << 5) ++#define CTRL_P1EN (1 << 4) ++#define CTRL_P0F16 (1 << 1) ++#define CTRL_P0EN (1 << 0) ++ ++/* ++ * Convert a PIO mode and cycle time to the required on/off ++ * times for the interface. This has protection against run-away ++ * timings. ++ */ ++static unsigned int get_timing_sl82c105(ide_pio_data_t *p) ++{ ++ unsigned int cmd_on; ++ unsigned int cmd_off; ++ ++ cmd_on = (ide_pio_timings[p->pio_mode].active_time + 29) / 30; ++ cmd_off = (p->cycle_time - 30 * cmd_on + 29) / 30; ++ ++ if (cmd_on > 32) ++ cmd_on = 32; ++ if (cmd_on == 0) ++ cmd_on = 1; ++ ++ if (cmd_off > 32) ++ cmd_off = 32; ++ if (cmd_off == 0) ++ cmd_off = 1; ++ ++ return (cmd_on - 1) << 8 | (cmd_off - 1) | (p->use_iordy ? 0x40 : 0x00); ++} ++ ++/* ++ * Configure the drive and chipset for PIO ++ */ ++static void config_for_pio(ide_drive_t *drive, int pio, int report) ++{ ++ ide_hwif_t *hwif = HWIF(drive); ++ struct pci_dev *dev = hwif->pci_dev; ++ ide_pio_data_t p; ++ unsigned short drv_ctrl = 0x909; ++ unsigned int xfer_mode, reg; ++ ++ reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0); ++ ++ pio = ide_get_best_pio_mode(drive, pio, 5, &p); ++ ++ switch (pio) { ++ default: ++ case 0: xfer_mode = XFER_PIO_0; break; ++ case 1: xfer_mode = XFER_PIO_1; break; ++ case 2: xfer_mode = XFER_PIO_2; break; ++ case 3: xfer_mode = XFER_PIO_3; break; ++ case 4: xfer_mode = XFER_PIO_4; break; ++ } ++ ++ if (ide_config_drive_speed(drive, xfer_mode) == 0) ++ drv_ctrl = get_timing_sl82c105(&p); ++ ++ if (drive->using_dma == 0) { ++ /* ++ * If we are actually using MW DMA, then we can not ++ * reprogram the interface drive control register. ++ */ ++ pci_write_config_word(dev, reg, drv_ctrl); ++ pci_read_config_word(dev, reg, &drv_ctrl); ++ ++ if (report) { ++ printk("%s: selected %s (%dns) (%04X)\n", drive->name, ++ ide_xfer_verbose(xfer_mode), p.cycle_time, drv_ctrl); ++ } ++ } ++} ++ ++/* ++ * Configure the drive and the chipset for DMA ++ */ ++static int config_for_dma(ide_drive_t *drive) ++{ ++ ide_hwif_t *hwif = HWIF(drive); ++ struct pci_dev *dev = hwif->pci_dev; ++ unsigned short drv_ctrl = 0x909; ++ unsigned int reg; ++ ++ reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0); ++ ++ if (ide_config_drive_speed(drive, XFER_MW_DMA_2) == 0) ++ drv_ctrl = 0x0240; ++ ++ pci_write_config_word(dev, reg, drv_ctrl); ++ ++ return 0; ++} ++ ++ ++/* ++ * Check to see if the drive and ++ * chipset is capable of DMA mode ++ */ ++static int sl82c105_check_drive(ide_drive_t *drive) ++{ ++ ide_dma_action_t dma_func = ide_dma_off_quietly; ++ ++ do { ++ struct hd_driveid *id = drive->id; ++ ide_hwif_t *hwif = HWIF(drive); ++ ++ if (!hwif->autodma) ++ break; ++ ++ if (!id || !(id->capability & 1)) ++ break; ++ ++ /* Consult the list of known "bad" drives */ ++ if (ide_dmaproc(ide_dma_bad_drive, drive)) { ++ dma_func = ide_dma_off; ++ break; ++ } ++ ++ if (id->field_valid & 2) { ++ if (id->dma_mword & 7 || id->dma_1word & 7) ++ dma_func = ide_dma_on; ++ break; ++ } ++ ++ if (ide_dmaproc(ide_dma_good_drive, drive)) { ++ dma_func = ide_dma_on; ++ break; ++ } ++ } while (0); ++ ++ return HWIF(drive)->dmaproc(dma_func, drive); ++} ++ ++/* ++ * The SL82C105 holds off all IDE interrupts while in DMA mode until ++ * all DMA activity is completed. Sometimes this causes problems (eg, ++ * when the drive wants to report an error condition). ++ * ++ * 0x7e is a "chip testing" register. Bit 2 resets the DMA controller ++ * state machine. We need to kick this to work around various bugs. ++ */ ++static inline void sl82c105_reset_host(struct pci_dev *dev) ++{ ++ u16 val; ++ ++ pci_read_config_word(dev, 0x7e, &val); ++ pci_write_config_word(dev, 0x7e, val | (1 << 2)); ++ pci_write_config_word(dev, 0x7e, val & ~(1 << 2)); ++} ++ ++/* ++ * If we get an IRQ timeout, it might be that the DMA state machine ++ * got confused. Fix from Todd Inglett. Details from Winbond. ++ * ++ * This function is called when the IDE timer expires, the drive ++ * indicates that it is READY, and we were waiting for DMA to complete. ++ */ ++static int sl82c105_lostirq(ide_drive_t *drive) ++{ ++ ide_hwif_t *hwif = HWIF(drive); ++ struct pci_dev *dev = hwif->pci_dev; ++ u32 val, mask = hwif->channel ? CTRL_IDE_IRQB : CTRL_IDE_IRQA; ++ unsigned long dma_base = hwif->dma_base; ++ ++ printk("sl82c105: lost IRQ: resetting host\n"); ++ ++ /* ++ * Check the raw interrupt from the drive. ++ */ ++ pci_read_config_dword(dev, 0x40, &val); ++ if (val & mask) ++ printk("sl82c105: drive was requesting IRQ, but host lost it\n"); ++ ++ /* ++ * Was DMA enabled? If so, disable it - we're resetting the ++ * host. The IDE layer will be handling the drive for us. ++ */ ++ val = inb(dma_base); ++ if (val & 1) { ++ outb(val & ~1, dma_base); ++ printk("sl82c105: DMA was enabled\n"); ++ } ++ ++ sl82c105_reset_host(dev); ++ ++ /* ide_dmaproc would return 1, so we do as well */ ++ return 1; ++} ++ ++/* ++ * ATAPI devices can cause the SL82C105 DMA state machine to go gaga. ++ * Winbond recommend that the DMA state machine is reset prior to ++ * setting the bus master DMA enable bit. ++ * ++ * The generic IDE core will have disabled the BMEN bit before this ++ * function is called. ++ */ ++static void sl82c105_before_bm_enable(ide_drive_t *drive) ++{ ++ ide_hwif_t *hwif = HWIF(drive); ++ struct pci_dev *dev = hwif->pci_dev; ++ ++ sl82c105_reset_host(dev); ++} ++ ++/* ++ * Our very own dmaproc. We need to intercept various calls ++ * to fix up the SL82C105 specific behaviour. ++ */ ++static int sl82c105_dmaproc(ide_dma_action_t func, ide_drive_t *drive) ++{ ++ switch (func) { ++ case ide_dma_check: ++ return sl82c105_check_drive(drive); ++ ++ case ide_dma_on: ++ if (config_for_dma(drive)) ++ func = ide_dma_off; ++ /* fall through */ ++ ++ case ide_dma_off_quietly: ++ case ide_dma_off: ++ config_for_pio(drive, 4, 0); ++ break; ++ ++ case ide_dma_read: ++ case ide_dma_write: ++ case ide_dma_begin: ++ case ide_dma_timeout: ++ sl82c105_before_bm_enable(drive); ++ break; ++ ++ case ide_dma_lostirq: ++ return sl82c105_lostirq(drive); ++ ++ default: ++ break; ++ } ++ return ide_dmaproc(func, drive); ++} ++ ++/* ++ * We only deal with PIO mode here - DMA mode 'using_dma' is not ++ * initialised at the point that this function is called. ++ */ ++static void tune_sl82c105(ide_drive_t *drive, byte pio) ++{ ++ config_for_pio(drive, pio, 1); ++ ++ /* ++ * We support 32-bit I/O on this interface, and it ++ * doesn't have problems with interrupts. ++ */ ++ drive->io_32bit = 1; ++ drive->unmask = 1; ++} ++ ++/* ++ * Return the revision of the Winbond bridge ++ * which this function is part of. ++ */ ++static unsigned int sl82c105_bridge_revision(struct pci_dev *dev) ++{ ++ struct pci_dev *bridge; ++ unsigned char rev; ++ ++ /* ++ * The bridge should be part of the same device, but function 0. ++ */ ++ bridge = pci_find_slot(dev->bus->number, ++ PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); ++ if (!bridge) ++ return -1; ++ ++ /* ++ * Make sure it is a Winbond 553 and is an ISA bridge. ++ */ ++ if (bridge->vendor != PCI_VENDOR_ID_WINBOND || ++ bridge->device != PCI_DEVICE_ID_WINBOND_83C553 || ++ bridge->class >> 8 != PCI_CLASS_BRIDGE_ISA) ++ return -1; ++ ++ /* ++ * We need to find function 0's revision, not function 1 ++ */ ++ pci_read_config_byte(bridge, PCI_REVISION_ID, &rev); ++ ++ return rev; ++} ++ ++/* ++ * Enable the PCI device ++ */ ++unsigned int __init pci_init_sl82c105(struct pci_dev *dev, const char *msg) ++{ ++ u32 val; ++ ++ pci_read_config_dword(dev, 0x40, &val); ++ val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1EN | CTRL_P1F16; ++ pci_write_config_dword(dev, 0x40, val); ++ ++ return dev->irq; ++} ++ ++void __init dma_init_sl82c105(ide_hwif_t *hwif, unsigned long dma_base) ++{ ++ unsigned int bridge_rev; ++ byte dma_state; ++ ++ dma_state = inb(dma_base + 2); ++ bridge_rev = sl82c105_bridge_revision(hwif->pci_dev); ++ if (bridge_rev <= 5) { ++ hwif->autodma = 0; ++ hwif->drives[0].autotune = 1; ++ hwif->drives[1].autotune = 1; ++ printk(" %s: Winbond 553 bridge revision %d, BM-DMA disabled\n", ++ hwif->name, bridge_rev); ++ dma_state &= ~0x60; ++ } else { ++ dma_state |= 0x60; ++ hwif->autodma = 1; ++ } ++ outb(dma_state, dma_base + 2); ++ ++ ide_setup_dma(hwif, dma_base, 8); ++ ++ if (bridge_rev <= 5) ++ hwif->dmaproc = NULL; ++ else ++ hwif->dmaproc = sl82c105_dmaproc; ++} ++ ++/* ++ * Initialise the chip ++ */ ++void __init ide_init_sl82c105(ide_hwif_t *hwif) ++{ ++ hwif->tuneproc = tune_sl82c105; ++} ++ +diff -urN linux-2.4.26/drivers/input/Config.in linux-2.4.26-vrs1/drivers/input/Config.in +--- linux-2.4.26/drivers/input/Config.in 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/input/Config.in 2004-02-23 22:53:55.000000000 +0000 +@@ -15,5 +15,6 @@ + dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_INPUT + dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_INPUT + dep_tristate ' User level driver support' CONFIG_INPUT_UINPUT $CONFIG_INPUT ++dep_tristate ' MX1 touchscreen support' CONFIG_INPUT_MX1TS $CONFIG_INPUT_MOUSEDEV $CONFIG_ARCH_MX1ADS + + endmenu +diff -urN linux-2.4.26/drivers/input/Makefile linux-2.4.26-vrs1/drivers/input/Makefile +--- linux-2.4.26/drivers/input/Makefile 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/input/Makefile 2004-02-23 22:53:03.000000000 +0000 +@@ -24,6 +24,7 @@ + obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o + obj-$(CONFIG_INPUT_JOYDEV) += joydev.o + obj-$(CONFIG_INPUT_EVDEV) += evdev.o ++obj-$(CONFIG_INPUT_MX1TS) += mx1ts.o + obj-$(CONFIG_INPUT_UINPUT) += uinput.o + + # The global Rules.make. +diff -urN linux-2.4.26/drivers/input/mx1ts.c linux-2.4.26-vrs1/drivers/input/mx1ts.c +--- linux-2.4.26/drivers/input/mx1ts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/input/mx1ts.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,508 @@ ++/* ++ * linux/drivers/misc/mx1ts.c ++ * ++ * Copyright (C) 2003 Blue Mug, Inc. for Motorola, Inc. ++ * ++ * Cloned from ucb1x00_ts.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#include "mx1ts.h" ++ ++#define DEV_IRQ_ID "mx1-ts" ++ ++struct mx1_ts { ++ struct input_dev idev; ++#ifdef CONFIG_PM ++ struct pm_dev *pmdev; ++#endif ++ ++ wait_queue_head_t irq_wait; ++ struct completion init_exit; ++ int use_count; ++ u16 x_res; ++ u16 y_res; ++ ++ int restart:1; ++}; ++ ++static struct mx1_ts mx1ts; ++static u8 mx1_performing_auto_calibration = 0; ++static u16 mx1_cal_auto_zero = 0; ++static u16 mx1_cal_range_x = 0; ++static u16 mx1_cal_range_y = 0; ++ ++static int mx1_ts_startup(struct mx1_ts *ts); ++static void mx1_ts_shutdown(struct mx1_ts *ts); ++ ++static void mx1_ts_pendata_int(int irq, void *dev_id, struct pt_regs *regs); ++static void mx1_ts_touch_int(int irq, void *dev_id, struct pt_regs *regs); ++static void mx1_ts_compare_int(int irq, void *dev_id, struct pt_regs *regs); ++ ++static void mx1_ts_enable_pen_touch_interrupt(void); ++static void mx1_ts_disable_pen_touch_interrupt(void); ++static void mx1_ts_enable_pen_up_interrupt(void); ++static void mx1_ts_disable_pen_up_interrupt(void); ++static void mx1_ts_enable_auto_sample(void); ++static void mx1_ts_disable_auto_sample(void); ++static void mx1_ts_start_auto_calibration(void); ++ ++static inline void mx1_reg_write(unsigned int reg, unsigned int val) ++{ ++ *((volatile unsigned int *)reg) = val; ++} ++ ++static inline unsigned int mx1_reg_read(unsigned int reg) ++{ ++ return *((volatile unsigned int *)reg); ++} ++ ++static inline void mx1_reg_clear_bit(unsigned int reg, unsigned int bit) ++{ ++ *((volatile unsigned int *)reg) &= ~bit; ++} ++ ++static inline void mx1_reg_set_bit(unsigned int reg, unsigned int bit) ++{ ++ *((volatile unsigned int *)reg) |= bit; ++} ++ ++static inline void mx1_ts_evt_add(struct mx1_ts *ts, u16 pressure, u16 x, u16 y) ++{ ++ input_report_abs(&ts->idev, ABS_X, (int)x - 32768); ++ input_report_abs(&ts->idev, ABS_Y, (int)y - 32768); ++ input_report_abs(&ts->idev, ABS_PRESSURE, (int)pressure); ++} ++ ++static inline void mx1_ts_flush_fifo(void) ++{ ++ int i; ++ for (i = 0; i < 12; i++) ++ if (mx1_reg_read(ASP_ISTATR) & (ASP_PFF | ASP_PDR)) ++ mx1_reg_read(ASP_PADFIFO); ++} ++ ++static int mx1_ts_open(struct input_dev *idev) ++{ ++ struct mx1_ts *ts = (struct mx1_ts *)idev; ++ ++ mx1_performing_auto_calibration = 0; ++ return mx1_ts_startup(ts); ++} ++ ++static void mx1_ts_close(struct input_dev *idev) ++{ ++ struct mx1_ts *ts = (struct mx1_ts *)idev; ++ ++ mx1_ts_shutdown(ts); ++} ++ ++static inline int mx1_ts_enable_irqs(void) ++{ ++ int result; ++ ++ result = request_irq(ASP_PENDATA_IRQ, ++ mx1_ts_pendata_int, ++ SA_INTERRUPT, ++ DEV_IRQ_ID, ++ DEV_IRQ_ID); ++ if (result) { ++ printk("Couldn't request pen data IRQ.\n"); ++ return result; ++ } ++ ++ result = request_irq(ASP_TOUCH_IRQ, ++ mx1_ts_touch_int, ++ SA_INTERRUPT, ++ DEV_IRQ_ID, ++ DEV_IRQ_ID); ++ if (result) { ++ printk("Couldn't request pen touch IRQ.\n"); ++ free_irq(ASP_PENDATA_IRQ, DEV_IRQ_ID); ++ return result; ++ } ++ ++ return result; ++} ++ ++static inline int mx1_ts_disable_irqs(void) ++{ ++ free_irq(ASP_PENDATA_IRQ, DEV_IRQ_ID); ++ free_irq(ASP_TOUCH_IRQ, DEV_IRQ_ID); ++ ++ return 0; ++} ++ ++static inline int mx1_ts_register(struct mx1_ts *ts) ++{ ++ ts->idev.name = "Touchscreen panel"; ++ ts->idev.open = mx1_ts_open; ++ ts->idev.close = mx1_ts_close; ++ ++ __set_bit(EV_ABS, ts->idev.evbit); ++ __set_bit(ABS_X, ts->idev.absbit); ++ __set_bit(ABS_Y, ts->idev.absbit); ++ __set_bit(ABS_PRESSURE, ts->idev.absbit); ++ ++ ts->idev.absmin[ABS_X] = 0; ++ ts->idev.absmax[ABS_X] = (u32)0x0000FFFF; ++ ts->idev.absfuzz[ABS_X] = 50; ++ ts->idev.absflat[ABS_X] = 0; ++ ++ ts->idev.absmin[ABS_Y] = 0; ++ ts->idev.absmax[ABS_Y] = (u32)0x0000FFFF; ++ ts->idev.absfuzz[ABS_Y] = 50; ++ ts->idev.absflat[ABS_Y] = 0; ++ ++ input_register_device(&ts->idev); ++ ++ return 0; ++} ++ ++static inline void mx1_ts_deregister(struct mx1_ts *ts) ++{ ++ input_unregister_device(&ts->idev); ++} ++ ++/* ++ * Handle the touch interrupt, generated when the pen is pressed/ ++ * released. ++ */ ++static void mx1_ts_touch_int(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ /* Clear the interrupt. */ ++ mx1_reg_set_bit(ASP_ISTATR, ASP_PEN); ++ ++ mx1_ts_disable_pen_touch_interrupt(); ++ mx1_ts_start_auto_calibration(); ++ mx1_ts_enable_pen_up_interrupt(); ++} ++ ++/* ++ * Handle the pen data ready interrupt, generated when pen data is ++ * in the FIFO. ++ */ ++static void mx1_ts_pendata_int(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ static unsigned int auto_zero, pen_x, pen_y, pen_u; ++ ++ if (mx1_reg_read(ASP_ISTATR) & 0x400) { ++ mx1_reg_set_bit(ASP_ISTATR, 0x400); ++ ++ mx1_ts_disable_auto_sample(); ++ mx1_ts_disable_pen_up_interrupt(); ++ mx1_ts_enable_pen_touch_interrupt(); ++ ++ mx1_ts_evt_add(&mx1ts, 0, pen_x, pen_y); ++ ++ mx1_ts_flush_fifo(); ++ ++ return; ++ } ++ ++ if (mx1_performing_auto_calibration) { ++ unsigned int value; ++ ++ mx1_cal_auto_zero = mx1_reg_read(ASP_PADFIFO) & 0xFFFF; ++ mx1_cal_range_x = mx1_reg_read(ASP_PADFIFO) & 0xFFFF; ++ mx1_cal_range_y = mx1_reg_read(ASP_PADFIFO) & 0xFFFF; ++ ++ if ((mx1_cal_auto_zero >= mx1_cal_range_x) || ++ (mx1_cal_auto_zero >= mx1_cal_range_y)) { ++ /* Invalid data. */ ++ mx1_ts_start_auto_calibration(); ++ return; ++ } ++ ++ mx1_cal_range_x -= mx1_cal_auto_zero; ++ mx1_cal_range_y -= mx1_cal_auto_zero; ++ ++ value = mx1_reg_read(ASP_ACNTLCR); ++ value &= ~0x04000000; /* XXX Undocumented. */ ++ mx1_reg_write(ASP_ACNTLCR, value); ++ ++ mx1_performing_auto_calibration = 0; ++ ++ mx1_ts_enable_auto_sample(); ++ } else { ++ /* There could be more than one sample in the FIFO, but we're ++ * only going to read one per call. The interrupt will be ++ * generated as long as there is data in the FIFO. */ ++ ++ if ((mx1_reg_read(ASP_ISTATR) & ASP_PDR) != ASP_PDR) { ++ return; ++ } ++ ++ auto_zero = mx1_reg_read(ASP_PADFIFO); ++ if (auto_zero > (mx1_cal_auto_zero + 0x200)) { ++ return; ++ } ++ ++ pen_x = mx1_reg_read(ASP_PADFIFO); ++ pen_y = mx1_reg_read(ASP_PADFIFO); ++ pen_u = mx1_reg_read(ASP_PADFIFO); ++ ++ pen_x = (u32)(((pen_x - mx1_cal_auto_zero) << 16) / ++ mx1_cal_range_x); ++ pen_y = (u32)(((pen_y - mx1_cal_auto_zero) << 16) / ++ mx1_cal_range_y); ++ ++ mx1_ts_evt_add(&mx1ts, pen_u, pen_x, pen_y); ++ } ++} ++ ++static void mx1_ts_reset_asp(void) ++{ ++ unsigned int value; ++ ++ mx1_ts_flush_fifo(); ++ ++ /* Soft reset the ASP module */ ++ mx1_reg_write(ASP_ACNTLCR, ASP_SWRST); ++ ++ /* Read back the reset value of the control register */ ++ value = mx1_reg_read(ASP_ACNTLCR); ++ ++ /* Enable the clock and wait for a short while */ ++ value |= ASP_CLKEN; ++ mx1_reg_write(ASP_ACNTLCR, value); ++ udelay(100); ++ ++ /* Set the value of the conrtol register. */ ++ value = ASP_CLKEN | ASP_NM | ASP_SW6 | ASP_BGE; ++ mx1_reg_write(ASP_ACNTLCR, value); ++ ++ /* Set the clock divide ratio to 2. */ ++ mx1_reg_write(ASP_CLKDIV, 0x01); ++ ++ /* Set the sample rate control register. These values should yield ++ * about 150 samples per second, which seems to give good smooth ++ * lines. */ ++ value = (0x2 << ASP_DMCNT_SCALE) | /* Decimation ratio is 3 */ ++ (0x1 << ASP_IDLECNT_SCALE) | /* Idle count is 1 clock */ ++ (0x2 << ASP_DSCNT_SCALE); /* Data setup is 2 clocks */ ++ mx1_reg_write(ASP_PSMPLRG, value); ++ ++ /* Disable the compare function. */ ++ mx1_reg_write(ASP_CMPCNTL, 0); ++} ++ ++static void mx1_ts_enable_auto_sample(void) ++{ ++ unsigned int value; ++ ++ mx1_ts_flush_fifo(); ++ ++ value = mx1_reg_read(ASP_ACNTLCR); ++ ++ /* Set the mode to X then Y */ ++ value &= ~ASP_MODE_MASK; ++ value |= ASP_MODE_ONLY_Y; ++ ++ /* Enable auto zero. */ ++ value |= ASP_AZE; ++ ++ /* Enable auto sample. */ ++ value |= ASP_AUTO; ++ ++ /* Enable pen A/D. */ ++ value |= ASP_PADE; ++ mx1_reg_write(ASP_ACNTLCR, value); ++ ++ /* Enable pen data ready and full interrupt. */ ++ value = mx1_reg_read(ASP_ICNTLR); ++ value |= ASP_PFFE | ASP_PDRE; ++ mx1_reg_write(ASP_ICNTLR, value); ++} ++ ++static void mx1_ts_disable_auto_sample(void) ++{ ++ unsigned int value; ++ ++ value = mx1_reg_read(ASP_ACNTLCR); ++ ++ /* Set the mode to none */ ++ value &= ~ASP_MODE_MASK; ++ ++ /* Disable auto zero. */ ++ value &= ~ASP_AZE; ++ ++ /* Disable auto sample. */ ++ value &= ~ASP_AUTO; ++ ++ /* Disable pen A/D. */ ++ value &= ~ASP_PADE; ++ mx1_reg_write(ASP_ACNTLCR, value); ++ ++ /* Disable pen data ready and full interrupt. */ ++ value = mx1_reg_read(ASP_ICNTLR); ++ value &= ~(ASP_PFFE | ASP_PDRE); ++ mx1_reg_write(ASP_ICNTLR, value); ++} ++ ++static void mx1_ts_enable_pen_touch_interrupt(void) ++{ ++ unsigned int value; ++ ++ /* Enable pen touch interrupt. */ ++ value = mx1_reg_read(ASP_ICNTLR); ++ value |= ASP_EDGE | ASP_PIRQE; ++ mx1_reg_write(ASP_ICNTLR, value); ++} ++ ++static void mx1_ts_disable_pen_touch_interrupt(void) ++{ ++ unsigned int value; ++ ++ /* Enable pen touch interrupt. */ ++ value = mx1_reg_read(ASP_ICNTLR); ++ value &= ~ASP_PIRQE; ++ mx1_reg_write(ASP_ICNTLR, value); ++} ++ ++static void mx1_ts_enable_pen_up_interrupt(void) ++{ ++ unsigned int value; ++ ++ /* Enable pen up interrupt. XXX: This feature is undocumented. */ ++ value = mx1_reg_read(ASP_ICNTLR); ++ value |= ASP_PUPE; ++ mx1_reg_write(ASP_ICNTLR, value); ++} ++ ++static void mx1_ts_disable_pen_up_interrupt(void) ++{ ++ unsigned int value; ++ ++ /* Enable pen up interrupt. XXX: This feature is undocumented. */ ++ value = mx1_reg_read(ASP_ICNTLR); ++ value &= ~ASP_PUPE; ++ mx1_reg_write(ASP_ICNTLR, value); ++} ++ ++static void mx1_ts_start_auto_calibration(void) ++{ ++ unsigned int value; ++ ++ mx1_performing_auto_calibration = 1; ++ ++ value = mx1_reg_read(ASP_ACNTLCR); ++ ++ /* Set the mode to X then Y */ ++ value &= ~ASP_MODE_MASK; ++ value |= ASP_MODE_ONLY_X; ++ ++ /* Enable auto zero. */ ++ value |= ASP_AZE; ++ ++ /* Enable auto calibrate. XXX: Undocumented bitfield. */ ++ value |= 0x04000000; ++ ++ /* Enable auto sample. */ ++ value |= ASP_AUTO; ++ ++ /* Enable pen A/D. */ ++ value |= ASP_PADE; ++ mx1_reg_write(ASP_ACNTLCR, value); ++ ++ /* Enable pen data ready and full interrupt. */ ++ value = mx1_reg_read(ASP_ICNTLR); ++ value |= ASP_PFFE | ASP_PDRE | ASP_PUPE; ++ mx1_reg_write(ASP_ICNTLR, value); ++} ++ ++static int mx1_ts_startup(struct mx1_ts *ts) ++{ ++ int ret = 0; ++ ++ if (ts->use_count++ != 0) ++ goto out; ++ ++ /* ++ * Reset the ASP. ++ */ ++ mx1_ts_reset_asp(); ++ ++ ++ /* ++ * XXX: Figure out if we need this... ++ * If we do this at all, we should allow the user to ++ * measure and read the X and Y resistance at any time. ++ */ ++ //ts->x_res = mx1_ts_read_xres(ts); ++ //ts->y_res = mx1_ts_read_yres(ts); ++ ++ mx1_ts_enable_pen_touch_interrupt(); ++ ++ out: ++ if (ret) ++ ts->use_count--; ++ return ret; ++} ++ ++/* ++ * Release touchscreen resources. Disable IRQs. ++ */ ++static void mx1_ts_shutdown(struct mx1_ts *ts) ++{ ++ if (--ts->use_count == 0) { ++ unsigned int value; ++ ++ /* Turn off the ADC and associated circuitry. */ ++ value = mx1_reg_read(ASP_ACNTLCR); ++ value &= !(ASP_CLKEN | ASP_PADE | ASP_BGE); ++ mx1_reg_write(ASP_ACNTLCR, value); ++ } ++} ++ ++/* ++ * Initialization. ++ */ ++static int __init mx1_ts_init(void) ++{ ++ int ret = 0; ++ struct mx1_ts *ts = &mx1ts; ++ ++ mx1_ts_reset_asp(); ++ ++ /* ++ * Enable the IRQ's ++ */ ++ if ((ret = mx1_ts_enable_irqs())) ++ return ret; ++ ++ return mx1_ts_register(ts); ++} ++ ++static void __exit mx1_ts_exit(void) ++{ ++ struct mx1_ts *ts = &mx1ts; ++ ++ mx1_ts_disable_irqs(); ++ mx1_ts_deregister(ts); ++} ++ ++module_init(mx1_ts_init); ++module_exit(mx1_ts_exit); ++ ++MODULE_AUTHOR("Jon McClintock "); ++MODULE_DESCRIPTION("MX1 touchscreen driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/input/mx1ts.h linux-2.4.26-vrs1/drivers/input/mx1ts.h +--- linux-2.4.26/drivers/input/mx1ts.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/input/mx1ts.h 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,108 @@ ++/* ++ * linux/drivers/misc/mx1ts.h ++ * ++ * Copyright (C) 2003 Blue Mug, Inc. for Motorola, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++/* Interrupt numbers */ ++#define ASP_COMPARE_IRQ 5 ++#define ASP_PENDATA_IRQ 33 ++#define ASP_TOUCH_IRQ 46 ++ ++/* Analog signal processor (ASP) control registers */ ++#define ASP_ACNTLCR 0xF0215010 /* Control register */ ++#define ASP_PSMPLRG 0xF0215014 /* Pen A/D sampe rate control */ ++#define ASP_CMPCNTL 0xF0215030 /* Compare control register */ ++#define ASP_ICNTLR 0xF0215018 /* Interrupt control register */ ++#define ASP_ISTATR 0xF021501C /* Interrupt status register */ ++#define ASP_PADFIFO 0xF0215000 /* Pen sample FIFO */ ++#define ASP_CLKDIV 0xF021502C /* Clock divide register */ ++ ++/* ASP control register bits */ ++#define ASP_CLKEN (1 << 25) /* Clock enable */ ++#define ASP_SWRST (1 << 23) /* Software reset */ ++#define ASP_U_SEL (1 << 21) /* U-channel resistor select */ ++#define ASP_AZ_SEL (1 << 20) /* Auto-zero position select */ ++#define ASP_LVM (1 << 19) /* Low voltage output */ ++#define ASP_NM (1 << 18) /* Normal voltage output */ ++#define ASP_HPM (1 << 17) /* High voltage output */ ++#define ASP_GLO (1 << 16) /* Low gain enable */ ++#define ASP_AZE (1 << 15) /* Auto-zero enable */ ++#define ASP_AUTO (1 << 14) /* Auto sampling */ ++#define ASP_SW8 (1 << 11) /* Switch control 8 */ ++#define ASP_SW7 (1 << 10) ++#define ASP_SW6 (1 << 9) ++#define ASP_SW5 (1 << 8) ++#define ASP_SW4 (1 << 7) ++#define ASP_SW3 (1 << 6) ++#define ASP_SW2 (1 << 5) ++#define ASP_SW1 (1 << 4) /* Switch control 1 */ ++#define ASP_VDAE (1 << 3) /* Voice D/A enable */ ++#define ASP_VADE (1 << 2) /* Voice A/D enable */ ++#define ASP_PADE (1 << 1) /* Pen A/D enable */ ++#define ASP_BGE (1 << 0) /* Bandgap enable */ ++ ++#define ASP_MODE_MASK 0x00003000 ++#define ASP_MODE_NONE 0x00000000 ++#define ASP_MODE_ONLY_X 0x00001000 ++#define ASP_MODE_ONLY_Y 0x00002000 ++#define ASP_MODE_ONLY_U 0x00003000 ++ ++/* ASP Pen A/D sample rate control register */ ++#define ASP_DMCNT_MASK (0x00007000) /* Decimation ratio count */ ++#define ASP_DMCNT_SCALE (12) ++#define ASP_BIT_SELECT_MASK (0x00000C00) /* Bit select */ ++#define ASP_BIT_SELECT_SCALE (10) ++#define ASP_IDLECNT_MASK (0x000003F0) /* Idle count */ ++#define ASP_IDLECNT_SCALE (4) ++#define ASP_DSCNT_MASK (0x0000000F) /* Data setup count */ ++#define ASP_DSCNT_SCALE (0) ++ ++/* ASP compare control register */ ++#define ASP_INT (1 << 19) /* Interrupt status */ ++#define ASP_CC (1 << 18) /* Trigger on greater than */ ++#define ASP_INSEL_MASK (0x00030000) ++#define ASP_INSEL_DISABLE (0x00000000) ++#define ASP_INSEL_X (0x00010000) ++#define ASP_INSEL_Y (0x00020000) ++#define ASP_INSEL_U (0x00030000) ++#define ASP_COMPARE_VAL_MASK (0x0000FFFF) ++#define ASP_COMPARE_VAL_SCALE (0) ++ ++/* ASP interrupt control register bits */ ++#define ASP_PUPE (1 << 10) /* Pen up XXX undocumented */ ++#define ASP_VDDMAE (1 << 8) /* VDAC FIFO empty DMA */ ++#define ASP_VADMAE (1 << 7) /* VADC FIFO full DMA */ ++#define ASP_POL (1 << 6) /* Pen interrupt polarity */ ++#define ASP_EDGE (1 << 5) /* Edge trigger enable */ ++#define ASP_PIRQE (1 << 4) /* Pen interrupt enable */ ++#define ASP_VDAFEE (1 << 3) /* VDAC FIFO empty interrupt */ ++#define ASP_VADFFE (1 << 2) /* VADC FIFO full interrupt */ ++#define ASP_PFFE (1 << 1) /* Pen FIFO full interrupt */ ++#define ASP_PDRE (1 << 0) /* Pen data ready interrupt */ ++ ++/* ASP interrupt/error status register bits */ ++#define ASP_PUP (1 << 10) /* Pen up XXX undocumented */ ++#define ASP_BGR (1 << 9) /* Bandgap ready */ ++#define ASP_VOV (1 << 8) /* Voice sample data overflow */ ++#define ASP_POV (1 << 7) /* Pen sample data overflow */ ++#define ASP_PEN (1 << 6) /* Pen interrupt */ ++#define ASP_VDAFF (1 << 5) /* VDAC FIFO full */ ++#define ASP_VDAFE (1 << 4) /* VDAC FIFO empty */ ++#define ASP_VADFF (1 << 3) /* VADC FIFO full */ ++#define ASP_VADDR (1 << 2) /* VADC data ready */ ++#define ASP_PFF (1 << 1) /* Pen sample FIFO full */ ++#define ASP_PDR (1 << 0) /* Pen data ready */ ++ ++/* ASP Clock divide register */ ++#define ASP_PADC_CLK_MASK (0x0000001F) ++#define ASP_PADC_CLK_SCALE (0) ++#define ASP_VADC_CLK_MASK (0x000003E0) ++#define ASP_VADC_CLK_SCALE (5) ++#define ASP_VDAC_CLK_MASK (0x00003C00) ++#define ASP_VDAC_CLK_SCALE (10) +diff -urN linux-2.4.26/drivers/l3/Config.in linux-2.4.26-vrs1/drivers/l3/Config.in +--- linux-2.4.26/drivers/l3/Config.in 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/l3/Config.in 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,21 @@ ++# ++# L3 bus configuration ++# ++mainmenu_option next_comment ++comment 'L3 serial bus support' ++ ++tristate 'L3 support' CONFIG_L3 ++dep_bool ' L3 bit-banging interfaces' CONFIG_L3_ALGOBIT $CONFIG_L3 ++dep_bool ' SA11x0 GPIO adapter' CONFIG_L3_BIT_SA1100_GPIO $CONFIG_L3_ALGOBIT $CONFIG_ARCH_SA1100 ++ ++comment 'Other L3 adapters' ++dep_bool ' SA1111 adapter' CONFIG_L3_SA1111 $CONFIG_L3 ++endmenu ++ ++# i2c must come before this ++if [ "$CONFIG_L3_BIT_SA1100_GPIO" = "y" -o \ ++ "$CONFIG_I2C_BIT_SA1100_GPIO" = "y" ]; then ++ define_bool CONFIG_BIT_SA1100_GPIO y ++else ++ define_bool CONFIG_BIT_SA1100_GPIO n ++fi +diff -urN linux-2.4.26/drivers/l3/Makefile linux-2.4.26-vrs1/drivers/l3/Makefile +--- linux-2.4.26/drivers/l3/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/l3/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,23 @@ ++# ++# Makefile for the L3 bus driver. ++# ++ ++O_TARGET := l3.o ++ ++export-objs := l3-core.o l3-algo-bit.o ++l3-y := ++l3-n := ++l3-drv-y := ++l3-drv-n := ++ ++# Link order: ++# (core, adapters, algorithms, drivers) then clients ++ ++l3-$(CONFIG_L3_ALGOBIT) += l3-algo-bit.o ++l3-$(CONFIG_BIT_SA1100_GPIO) += l3-bit-sa1100.o ++l3-$(CONFIG_L3_SA1111) += l3-sa1111.o ++ ++obj-$(CONFIG_L3) += l3-core.o $(l3-y) $(l3-drv-y) ++ ++include $(TOPDIR)/Rules.make ++ +diff -urN linux-2.4.26/drivers/l3/l3-algo-bit.c linux-2.4.26-vrs1/drivers/l3/l3-algo-bit.c +--- linux-2.4.26/drivers/l3/l3-algo-bit.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/l3/l3-algo-bit.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,175 @@ ++/* ++ * L3 bus algorithm module. ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Note that L3 buses can share the same pins as I2C buses, so we must ++ * _not_ generate an I2C start condition. An I2C start condition is ++ * defined as a high-to-low transition of the data line while the clock ++ * is high. Therefore, we must only change the data line while the ++ * clock is low. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define setdat(adap,val) adap->setdat(adap->data, val) ++#define setclk(adap,val) adap->setclk(adap->data, val) ++#define setmode(adap,val) adap->setmode(adap->data, val) ++#define setdatin(adap) adap->setdir(adap->data, 1) ++#define setdatout(adap) adap->setdir(adap->data, 0) ++#define getdat(adap) adap->getdat(adap->data) ++ ++/* ++ * Send one byte of data to the chip. Data is latched into the chip on ++ * the rising edge of the clock. ++ */ ++static void sendbyte(struct l3_algo_bit_data *adap, unsigned int byte) ++{ ++ int i; ++ ++ for (i = 0; i < 8; i++) { ++ setclk(adap, 0); ++ udelay(adap->data_hold); ++ setdat(adap, byte & 1); ++ udelay(adap->data_setup); ++ setclk(adap, 1); ++ udelay(adap->clock_high); ++ byte >>= 1; ++ } ++} ++ ++/* ++ * Send a set of bytes to the chip. We need to pulse the MODE line ++ * between each byte, but never at the start nor at the end of the ++ * transfer. ++ */ ++static void sendbytes(struct l3_algo_bit_data *adap, const char *buf, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) { ++ if (i) { ++ udelay(adap->mode_hold); ++ setmode(adap, 0); ++ udelay(adap->mode); ++ } ++ setmode(adap, 1); ++ udelay(adap->mode_setup); ++ sendbyte(adap, buf[i]); ++ } ++} ++ ++/* ++ * Read one byte of data from the chip. Data is latched into the chip on ++ * the rising edge of the clock. ++ */ ++static unsigned int readbyte(struct l3_algo_bit_data *adap) ++{ ++ unsigned int byte = 0; ++ int i; ++ ++ for (i = 0; i < 8; i++) { ++ setclk(adap, 0); ++ udelay(adap->data_hold + adap->data_setup); ++ setclk(adap, 1); ++ if (getdat(adap)) ++ byte |= 1 << i; ++ udelay(adap->clock_high); ++ } ++ ++ return byte; ++} ++ ++/* ++ * Read a set of bytes from the chip. We need to pulse the MODE line ++ * between each byte, but never at the start nor at the end of the ++ * transfer. ++ */ ++static void readbytes(struct l3_algo_bit_data *adap, char *buf, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) { ++ if (i) { ++ udelay(adap->mode_hold); ++ setmode(adap, 0); ++ } ++ setmode(adap, 1); ++ udelay(adap->mode_setup); ++ buf[i] = readbyte(adap); ++ } ++} ++ ++static int l3_xfer(struct l3_adapter *l3_adap, struct l3_msg msgs[], int num) ++{ ++ struct l3_algo_bit_data *adap = l3_adap->algo_data; ++ int i; ++ ++ /* ++ * If we share an I2C bus, ensure that it is in STOP mode ++ */ ++ setclk(adap, 1); ++ setdat(adap, 1); ++ setmode(adap, 1); ++ setdatout(adap); ++ udelay(adap->mode); ++ ++ for (i = 0; i < num; i++) { ++ struct l3_msg *pmsg = &msgs[i]; ++ ++ if (!(pmsg->flags & L3_M_NOADDR)) { ++ setmode(adap, 0); ++ udelay(adap->mode_setup); ++ sendbyte(adap, pmsg->addr); ++ udelay(adap->mode_hold); ++ } ++ ++ if (pmsg->flags & L3_M_RD) { ++ setdatin(adap); ++ readbytes(adap, pmsg->buf, pmsg->len); ++ } else { ++ setdatout(adap); ++ sendbytes(adap, pmsg->buf, pmsg->len); ++ } ++ } ++ ++ /* ++ * Ensure that we leave the bus in I2C stop mode. ++ */ ++ setclk(adap, 1); ++ setdat(adap, 1); ++ setmode(adap, 0); ++ setdatin(adap); ++ ++ return num; ++} ++ ++static struct l3_algorithm l3_bit_algo = { ++ name: "L3 bit-shift algorithm", ++ xfer: l3_xfer, ++}; ++ ++int l3_bit_add_bus(struct l3_adapter *adap) ++{ ++ adap->algo = &l3_bit_algo; ++ return l3_add_adapter(adap); ++} ++ ++int l3_bit_del_bus(struct l3_adapter *adap) ++{ ++ return l3_del_adapter(adap); ++} ++ ++EXPORT_SYMBOL(l3_bit_add_bus); ++EXPORT_SYMBOL(l3_bit_del_bus); +diff -urN linux-2.4.26/drivers/l3/l3-bit-sa1100.c linux-2.4.26-vrs1/drivers/l3/l3-bit-sa1100.c +--- linux-2.4.26/drivers/l3/l3-bit-sa1100.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/l3/l3-bit-sa1100.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,277 @@ ++/* ++ * linux/drivers/l3/l3-bit-sa1100.c ++ * ++ * Copyright (C) 2001 Russell King ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This is a combined I2C and L3 bus driver. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define NAME "l3-bit-sa1100-gpio" ++ ++struct bit_data { ++ unsigned int sda; ++ unsigned int scl; ++ unsigned int l3_mode; ++}; ++ ++static int getsda(void *data) ++{ ++ struct bit_data *bits = data; ++ ++ return GPLR & bits->sda; ++} ++ ++#ifdef CONFIG_I2C_BIT_SA1100_GPIO ++static void i2c_setsda(void *data, int state) ++{ ++ struct bit_data *bits = data; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ if (state) ++ GPDR &= ~bits->sda; ++ else { ++ GPCR = bits->sda; ++ GPDR |= bits->sda; ++ } ++ local_irq_restore(flags); ++} ++ ++static void i2c_setscl(void *data, int state) ++{ ++ struct bit_data *bits = data; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ if (state) ++ GPDR &= ~bits->scl; ++ else { ++ GPCR = bits->scl; ++ GPDR |= bits->scl; ++ } ++ local_irq_restore(flags); ++} ++ ++static int i2c_getscl(void *data) ++{ ++ struct bit_data *bits = data; ++ ++ return GPLR & bits->scl; ++} ++ ++static struct i2c_algo_bit_data i2c_bit_data = { ++ setsda: i2c_setsda, ++ setscl: i2c_setscl, ++ getsda: getsda, ++ getscl: i2c_getscl, ++ udelay: 10, ++ mdelay: 10, ++ timeout: 100, ++}; ++ ++static struct i2c_adapter i2c_adapter = { ++ name: NAME, ++ algo_data: &i2c_bit_data, ++// inc_use: i2c_inc_use, ++// dec_use: i2c_dec_use, ++}; ++ ++#define LOCK &i2c_adapter.lock ++ ++static int __init i2c_init(struct bit_data *bits) ++{ ++ i2c_bit_data.data = bits; ++ return i2c_bit_add_bus(&i2c_adapter); ++} ++ ++static void i2c_exit(void) ++{ ++ i2c_bit_del_bus(&i2c_adapter); ++} ++ ++#else ++static DECLARE_MUTEX(l3_lock); ++#define LOCK &l3_lock ++#define i2c_init(bits) (0) ++#define i2c_exit() do { } while (0) ++#endif ++ ++#ifdef CONFIG_L3_BIT_SA1100_GPIO ++/* ++ * iPAQs need the clock line driven hard high and low. ++ */ ++static void l3_setscl(void *data, int state) ++{ ++ struct bit_data *bits = data; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ if (state) ++ GPSR = bits->scl; ++ else ++ GPCR = bits->scl; ++ GPDR |= bits->scl; ++ local_irq_restore(flags); ++} ++ ++static void l3_setsda(void *data, int state) ++{ ++ struct bit_data *bits = data; ++ ++ if (state) ++ GPSR = bits->sda; ++ else ++ GPCR = bits->sda; ++} ++ ++static void l3_setdir(void *data, int in) ++{ ++ struct bit_data *bits = data; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ if (in) ++ GPDR &= ~bits->sda; ++ else ++ GPDR |= bits->sda; ++ local_irq_restore(flags); ++} ++ ++static void l3_setmode(void *data, int state) ++{ ++ struct bit_data *bits = data; ++ ++ if (state) ++ GPSR = bits->l3_mode; ++ else ++ GPCR = bits->l3_mode; ++} ++ ++static struct l3_algo_bit_data l3_bit_data = { ++ data: NULL, ++ setdat: l3_setsda, ++ setclk: l3_setscl, ++ setmode: l3_setmode, ++ setdir: l3_setdir, ++ getdat: getsda, ++ data_hold: 1, ++ data_setup: 1, ++ clock_high: 1, ++ mode_hold: 1, ++ mode_setup: 1, ++}; ++ ++static struct l3_adapter l3_adapter = { ++ owner: THIS_MODULE, ++ name: NAME, ++ algo_data: &l3_bit_data, ++ lock: LOCK, ++}; ++ ++static int __init l3_init(struct bit_data *bits) ++{ ++ l3_bit_data.data = bits; ++ return l3_bit_add_bus(&l3_adapter); ++} ++ ++static void __exit l3_exit(void) ++{ ++ l3_bit_del_bus(&l3_adapter); ++} ++#else ++#define l3_init(bits) (0) ++#define l3_exit() do { } while (0) ++#endif ++ ++static struct bit_data bit_data; ++ ++static int __init bus_init(void) ++{ ++ struct bit_data *bit = &bit_data; ++ unsigned long flags; ++ int ret; ++ ++ if (machine_is_assabet() || machine_is_pangolin()) { ++ bit->sda = GPIO_GPIO15; ++ bit->scl = GPIO_GPIO18; ++ bit->l3_mode = GPIO_GPIO17; ++ } ++ ++#if defined(CONFIG_SA1100_H3600) || defined(CONFIG_SA1100_H3100) ++ if (machine_is_h3600() || machine_is_h3100()) { ++ bit->sda = GPIO_H3600_L3_DATA; ++ bit->scl = GPIO_H3600_L3_CLOCK; ++ bit->l3_mode = GPIO_H3600_L3_MODE; ++ } ++#endif ++ ++#ifdef CONFIG_SA1100_STORK ++ if (machine_is_stork()) { ++ bit->sda = GPIO_STORK_L3_I2C_SDA; ++ bit->scl = GPIO_STORK_L3_I2C_SCL; ++ bit->l3_mode = GPIO_STORK_L3_MODE; ++ } ++#endif ++ ++ if (!bit->sda) ++ return -ENODEV; ++ ++ /* ++ * Default level for L3 mode is low. ++ * We set SCL and SDA high (i2c idle state). ++ */ ++ local_irq_save(flags); ++ GPDR &= ~(bit->scl | bit->sda); ++ GPCR = bit->l3_mode | bit->scl | bit->sda; ++ GPDR |= bit->l3_mode; ++ local_irq_restore(flags); ++ ++ if (machine_is_assabet()) { ++ /* ++ * Release reset on UCB1300, ADI7171 and UDA1341. We ++ * need to do this here so that we can communicate on ++ * the I2C/L3 buses. ++ */ ++ ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); ++ mdelay(1); ++ ASSABET_BCR_clear(ASSABET_BCR_CODEC_RST); ++ mdelay(1); ++ ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); ++ } ++ ++ ret = i2c_init(bit); ++ if (ret == 0 && bit->l3_mode) { ++ ret = l3_init(bit); ++ if (ret) ++ i2c_exit(); ++ } ++ ++ return ret; ++} ++ ++static void __exit bus_exit(void) ++{ ++ l3_exit(); ++ i2c_exit(); ++} ++ ++module_init(bus_init); ++module_exit(bus_exit); +diff -urN linux-2.4.26/drivers/l3/l3-core.c linux-2.4.26-vrs1/drivers/l3/l3-core.c +--- linux-2.4.26/drivers/l3/l3-core.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/l3/l3-core.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,377 @@ ++/* ++ * linux/drivers/l3/l3-core.c ++ * ++ * Copyright (C) 2001 Russell King ++ * ++ * General structure taken from i2c-core.c by Simon G. Vogl ++ * ++ * 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. ++ * ++ * See linux/Documentation/l3 for further documentation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static DECLARE_MUTEX(adapter_lock); ++static LIST_HEAD(adapter_list); ++ ++static DECLARE_MUTEX(driver_lock); ++static LIST_HEAD(driver_list); ++ ++/** ++ * l3_add_adapter - register a new L3 bus adapter ++ * @adap: l3_adapter structure for the registering adapter ++ * ++ * Make the adapter available for use by clients using name adap->name. ++ * The adap->adapters list is initialised by this function. ++ * ++ * Returns 0; ++ */ ++int l3_add_adapter(struct l3_adapter *adap) ++{ ++ INIT_LIST_HEAD(&adap->clients); ++ down(&adapter_lock); ++ list_add(&adap->adapters, &adapter_list); ++ up(&adapter_lock); ++ return 0; ++} ++ ++/** ++ * l3_del_adapter - unregister a L3 bus adapter ++ * @adap: l3_adapter structure to unregister ++ * ++ * Remove an adapter from the list of available L3 Bus adapters. ++ * ++ * Returns 0; ++ */ ++int l3_del_adapter(struct l3_adapter *adap) ++{ ++ down(&adapter_lock); ++ list_del(&adap->adapters); ++ up(&adapter_lock); ++ return 0; ++} ++ ++static struct l3_adapter *__l3_get_adapter(const char *name) ++{ ++ struct list_head *l; ++ ++ list_for_each(l, &adapter_list) { ++ struct l3_adapter *adap = list_entry(l, struct l3_adapter, adapters); ++ ++ if (strcmp(adap->name, name) == 0) ++ return adap; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * l3_get_adapter - get a reference to an adapter ++ * @name: driver name ++ * ++ * Obtain a l3_adapter structure for the specified adapter. If the adapter ++ * is not currently load, then load it. The adapter will be locked in core ++ * until all references are released via l3_put_adapter. ++ */ ++struct l3_adapter *l3_get_adapter(const char *name) ++{ ++ struct l3_adapter *adap; ++ int try; ++ ++ for (try = 0; try < 2; try ++) { ++ down(&adapter_lock); ++ adap = __l3_get_adapter(name); ++ if (adap && !try_inc_mod_count(adap->owner)) ++ adap = NULL; ++ up(&adapter_lock); ++ ++ if (adap) ++ break; ++ ++ if (try == 0) ++ request_module(name); ++ } ++ ++ return adap; ++} ++ ++/** ++ * l3_put_adapter - release a reference to an adapter ++ * @adap: driver to release reference ++ * ++ * Indicate to the L3 core that you no longer require the adapter reference. ++ * The adapter module may be unloaded when there are no references to its ++ * data structure. ++ * ++ * You must not use the reference after calling this function. ++ */ ++void l3_put_adapter(struct l3_adapter *adap) ++{ ++ if (adap && adap->owner) ++ __MOD_DEC_USE_COUNT(adap->owner); ++} ++ ++/** ++ * l3_add_driver - register a new L3 device driver ++ * @driver - driver structure to make available ++ * ++ * Make the driver available for use by clients using name driver->name. ++ * The driver->drivers list is initialised by this function. ++ * ++ * Returns 0; ++ */ ++int l3_add_driver(struct l3_driver *driver) ++{ ++ down(&driver_lock); ++ list_add(&driver->drivers, &driver_list); ++ up(&driver_lock); ++ return 0; ++} ++ ++/** ++ * l3_del_driver - unregister a L3 device driver ++ * @driver: driver to remove ++ * ++ * Remove an driver from the list of available L3 Bus device drivers. ++ * ++ * Returns 0; ++ */ ++int l3_del_driver(struct l3_driver *driver) ++{ ++ down(&driver_lock); ++ list_del(&driver->drivers); ++ up(&driver_lock); ++ return 0; ++} ++ ++static struct l3_driver *__l3_get_driver(const char *name) ++{ ++ struct list_head *l; ++ ++ list_for_each(l, &driver_list) { ++ struct l3_driver *drv = list_entry(l, struct l3_driver, drivers); ++ ++ if (strcmp(drv->name, name) == 0) ++ return drv; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * l3_get_driver - get a reference to a driver ++ * @name: driver name ++ * ++ * Obtain a l3_driver structure for the specified driver. If the driver is ++ * not currently load, then load it. The driver will be locked in core ++ * until all references are released via l3_put_driver. ++ */ ++struct l3_driver *l3_get_driver(const char *name) ++{ ++ struct l3_driver *drv; ++ int try; ++ ++ for (try = 0; try < 2; try ++) { ++ down(&adapter_lock); ++ drv = __l3_get_driver(name); ++ if (drv && !try_inc_mod_count(drv->owner)) ++ drv = NULL; ++ up(&adapter_lock); ++ ++ if (drv) ++ break; ++ ++ if (try == 0) ++ request_module(name); ++ } ++ ++ return drv; ++} ++ ++/** ++ * l3_put_driver - release a reference to a driver ++ * @drv: driver to release reference ++ * ++ * Indicate to the L3 core that you no longer require the driver reference. ++ * The driver module may be unloaded when there are no references to its ++ * data structure. ++ * ++ * You must not use the reference after calling this function. ++ */ ++void l3_put_driver(struct l3_driver *drv) ++{ ++ if (drv && drv->owner) ++ __MOD_DEC_USE_COUNT(drv->owner); ++} ++ ++/** ++ * l3_attach_client - attach a client to an adapter and driver ++ * @client: client structure to attach ++ * @adap: adapter (module) name ++ * @drv: driver (module) name ++ * ++ * Attempt to attach a client (a user of a device driver) to a particular ++ * driver and adapter. If the specified driver or adapter aren't registered, ++ * request_module is used to load the relevant modules. ++ * ++ * Returns 0 on success, or negative error code. ++ */ ++int l3_attach_client(struct l3_client *client, const char *adap, const char *drv) ++{ ++ struct l3_adapter *adapter = l3_get_adapter(adap); ++ struct l3_driver *driver = l3_get_driver(drv); ++ int ret = -ENOENT; ++ ++ if (!adapter) ++ printk(KERN_ERR "%s: unable to get adapter: %s\n", ++ __FUNCTION__, adap); ++ if (!driver) ++ printk(KERN_ERR "%s: unable to get driver: %s\n", ++ __FUNCTION__, drv); ++ ++ if (adapter && driver) { ++ ret = 0; ++ ++ client->adapter = adapter; ++ client->driver = driver; ++ ++ list_add(&client->__adap, &adapter->clients); ++ ++ if (driver->attach_client) ++ ret = driver->attach_client(client); ++ } ++ ++ if (ret) { ++ l3_put_driver(driver); ++ l3_put_adapter(adapter); ++ } ++ return ret; ++} ++ ++/** ++ * l3_detach_client - detach a client from an adapter and driver ++ * @client: client structure to detach ++ * ++ * Detach the client from the adapter and driver. ++ */ ++int l3_detach_client(struct l3_client *client) ++{ ++ struct l3_adapter *adapter = client->adapter; ++ struct l3_driver *driver = client->driver; ++ ++ driver->detach_client(client); ++ ++ client->adapter = NULL; ++ client->driver = NULL; ++ ++ l3_put_driver(driver); ++ l3_put_adapter(adapter); ++ ++ list_del(&client->__adap); ++ ++ return 0; ++} ++ ++/** ++ * l3_transfer - transfer information on an L3 bus ++ * @adap: adapter structure to perform transfer on ++ * @msgs: array of l3_msg structures describing transfer ++ * @num: number of l3_msg structures ++ * ++ * Transfer the specified messages to/from a device on the L3 bus. ++ * ++ * Returns number of messages successfully transferred, otherwise negative ++ * error code. ++ */ ++int l3_transfer(struct l3_adapter *adap, struct l3_msg msgs[], int num) ++{ ++ int ret = -ENOSYS; ++ ++ if (adap->algo->xfer) { ++ down(adap->lock); ++ ret = adap->algo->xfer(adap, msgs, num); ++ up(adap->lock); ++ } ++ return ret; ++} ++ ++/** ++ * l3_write - send data to a device on an L3 bus ++ * @client: registered client structure ++ * @addr: L3 bus address ++ * @buf: buffer for bytes to send ++ * @len: number of bytes to send ++ * ++ * Send len bytes pointed to by buf to device address addr on the L3 bus ++ * described by client. ++ * ++ * Returns the number of bytes transferred, or negative error code. ++ */ ++int l3_write(struct l3_client *client, int addr, const char *buf, int len) ++{ ++ struct l3_adapter *adap = client->adapter; ++ struct l3_msg msg; ++ int ret; ++ ++ msg.addr = addr; ++ msg.flags = 0; ++ msg.buf = (char *)buf; ++ msg.len = len; ++ ++ ret = l3_transfer(adap, &msg, 1); ++ return ret == 1 ? len : ret; ++} ++ ++/** ++ * l3_read - receive data from a device on an L3 bus ++ * @client: registered client structure ++ * @addr: L3 bus address ++ * @buf: buffer for bytes to receive ++ * @len: number of bytes to receive ++ * ++ * Receive len bytes from device address addr on the L3 bus described by ++ * client to a buffer pointed to by buf. ++ * ++ * Returns the number of bytes transferred, or negative error code. ++ */ ++int l3_read(struct l3_client *client, int addr, char *buf, int len) ++{ ++ struct l3_adapter *adap = client->adapter; ++ struct l3_msg msg; ++ int ret; ++ ++ msg.addr = addr; ++ msg.flags = L3_M_RD; ++ msg.buf = buf; ++ msg.len = len; ++ ++ ret = l3_transfer(adap, &msg, 1); ++ return ret == 1 ? len : ret; ++} ++ ++EXPORT_SYMBOL(l3_add_adapter); ++EXPORT_SYMBOL(l3_del_adapter); ++EXPORT_SYMBOL(l3_get_adapter); ++EXPORT_SYMBOL(l3_put_adapter); ++ ++EXPORT_SYMBOL(l3_add_driver); ++EXPORT_SYMBOL(l3_del_driver); ++EXPORT_SYMBOL(l3_get_driver); ++EXPORT_SYMBOL(l3_put_driver); ++ ++EXPORT_SYMBOL(l3_attach_client); ++EXPORT_SYMBOL(l3_detach_client); ++ ++EXPORT_SYMBOL(l3_transfer); ++EXPORT_SYMBOL(l3_write); ++EXPORT_SYMBOL(l3_read); +diff -urN linux-2.4.26/drivers/l3/l3-sa1111.c linux-2.4.26-vrs1/drivers/l3/l3-sa1111.c +--- linux-2.4.26/drivers/l3/l3-sa1111.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/l3/l3-sa1111.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,118 @@ ++/* ++ * L3 SA1111 algorithm/adapter module. ++ * ++ * By Russell King, ++ * gratuitously ripped from sa1111-uda1341.c by John Dorsey. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++static inline unsigned char l3_sa1111_recv_byte(unsigned char addr) ++{ ++ unsigned char dat; ++ ++ L3_CAR = addr; ++ while ((SASR0 & SASR0_L3RD) == 0) ++ mdelay(1); ++ dat = L3_CDR; ++ SASCR = SASCR_RDD; ++ return dat; ++} ++ ++static void l3_sa1111_recv_msg(struct l3_msg *msg) ++{ ++ int len = msg->len; ++ char *p = msg->buf; ++ ++ if (len > 1) { ++ SACR1 |= SACR1_L3MB; ++ while ((len--) > 1) ++ *p++ = l3_sa1111_recv_byte(msg->addr); ++ } ++ SACR1 &= ~SACR1_L3MB; ++ *p = l3_sa1111_recv_byte(msg->addr); ++} ++ ++static inline void l3_sa1111_send_byte(unsigned char addr, unsigned char dat) ++{ ++ L3_CAR = addr; ++ L3_CDR = dat; ++ while ((SASR0 & SASR0_L3WD) == 0) ++ mdelay(1); ++ SASCR = SASCR_DTS; ++} ++ ++static void l3_sa1111_send_msg(struct l3_msg *msg) ++{ ++ int len = msg->len; ++ char *p = msg->buf; ++ ++ if (len > 1) { ++ SACR1 |= SACR1_L3MB; ++ while ((len--) > 1) ++ l3_sa1111_send_byte(msg->addr, *p++); ++ } ++ SACR1 &= ~SACR1_L3MB; ++ l3_sa1111_send_byte(msg->addr, *p); ++} ++ ++static int l3_sa1111_xfer(struct l3_adapter *adap, struct l3_msg msgs[], int num) ++{ ++ int i; ++ ++ for (i = 0; i < num; i++) { ++ struct l3_msg *pmsg = &msgs[i]; ++ ++ if (pmsg->flags & L3_M_RD) ++ l3_sa1111_recv_msg(pmsg); ++ else ++ l3_sa1111_send_msg(pmsg); ++ } ++ ++ return num; ++} ++ ++static struct l3_algorithm l3_sa1111_algo = { ++ name: "L3 SA1111 algorithm", ++ xfer: l3_sa1111_xfer, ++}; ++ ++static DECLARE_MUTEX(sa1111_lock); ++ ++static struct l3_adapter l3_sa1111_adapter = { ++ owner: THIS_MODULE, ++ name: "l3-sa1111", ++ algo: &l3_sa1111_algo, ++ lock: &sa1111_lock, ++}; ++ ++static int __init l3_sa1111_init(void) ++{ ++ int ret = -ENODEV; ++ if ((machine_is_assabet() && machine_has_neponset()) || ++ machine_is_jornada720() || machine_is_accelent_sa() || ++ machine_is_badge4()) ++ ret = l3_add_adapter(&l3_sa1111_adapter); ++ return ret; ++} ++ ++static void __exit l3_sa1111_exit(void) ++{ ++ l3_del_adapter(&l3_sa1111_adapter); ++} ++ ++module_init(l3_sa1111_init); ++module_exit(l3_sa1111_exit); +diff -urN linux-2.4.26/drivers/media/video/Config.in linux-2.4.26-vrs1/drivers/media/video/Config.in +--- linux-2.4.26/drivers/media/video/Config.in 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/media/video/Config.in 2004-04-19 10:36:42.000000000 +0100 +@@ -52,5 +52,8 @@ + if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_HIGHMEM64G" != "y" ]; then + dep_tristate ' Sony Vaio Picturebook Motion Eye Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_MEYE $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_SONYPI + fi ++# unfortunately, this depends on having CONFIG_FB_CYBER2000 ++# set as well - we hook off of the VGA driver ++dep_tristate ' NetWinder Video for Linux (EXPERIMENTAL)' CONFIG_VIDEO_CYBERPRO $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL $CONFIG_ARCH_NETWINDER + + endmenu +diff -urN linux-2.4.26/drivers/media/video/Makefile linux-2.4.26-vrs1/drivers/media/video/Makefile +--- linux-2.4.26/drivers/media/video/Makefile 2004-02-27 20:03:25.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/media/video/Makefile 2004-02-23 22:56:20.000000000 +0000 +@@ -16,7 +16,7 @@ + obj-n := + obj- := + +-SUB_DIRS := ++SUB_DIRS := + MOD_SUB_DIRS := $(SUB_DIRS) + ALL_SUB_DIRS := $(SUB_DIRS) + +@@ -47,7 +47,8 @@ + obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o i2c-old.o + obj-$(CONFIG_VIDEO_ZORAN_BUZ) += saa7111.o saa7185.o + obj-$(CONFIG_VIDEO_ZORAN_DC10) += saa7110.o adv7175.o +-obj-$(CONFIG_VIDEO_ZORAN_LML33) += bt819.o bt856.o ++obj-$(CONFIG_VIDEO_CYBERPRO) += cyberpro.o i2c-old.o saa7111.o ++obj-$(CONFIG_VIDEO_LML33) += bt856.o bt819.o + obj-$(CONFIG_VIDEO_PMS) += pms.o + obj-$(CONFIG_VIDEO_PLANB) += planb.o + obj-$(CONFIG_VIDEO_VINO) += saa7191.o indycam.o vino.o +diff -urN linux-2.4.26/drivers/media/video/cyberpro.c linux-2.4.26-vrs1/drivers/media/video/cyberpro.c +--- linux-2.4.26/drivers/media/video/cyberpro.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/media/video/cyberpro.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,2091 @@ ++/* ++ * CyberPro 2000 video capture driver for the Rebel.com NetWinder ++ * ++ * (C) 1999-2000 Russell King ++ * ++ * Re-written from Rebel.com's vidcap driver. ++ * ++ * Architecture ++ * ------------ ++ * The NetWinder video capture consists of a SAA7111 video decoder chip ++ * connected to the CyberPro feature bus. The video data is captured to ++ * the VGA memory, where the CyberPro can overlay (by chromakeying) the ++ * data onto the VGA display. ++ * ++ * The CyberPro also has some nifty features, including a second overlay ++ * and picture in picture mode. We do not currently use these features. ++ * ++ * Power Saving ++ * ------------ ++ * Please note that rev.5 NetWinders have the ability to hold the SAA7111 ++ * decoder chip into reset, which saves power. The only time at which ++ * this is done is when the driver is unloaded, which implies that this ++ * is compiled as a module. ++ * ++ * In this case, you will want the kernel to automatically load this ++ * driver when required. Place the following line in /etc/modules.conf ++ * to enable this: ++ * ++ * alias char-major-81-0 cyberpro ++ * ++ * The relevant modules will be automatically loaded by modprobe on a ++ * as and when needed basis. ++ * ++ * Capture resolution ++ * ------------------ ++ * The maximum useful capture resolution is: ++ * 625-line UK: 716x576 ++ * 525-line US: ? ++ * ++ * Bugs ++ * ---- ++ * 1. The CyberPro chip seems to be prone to randomly scribbling over VGA ++ * memory [hopefully fixed with new capture enable/freeze stuff] ++ * 2. read()ing pauses video capture, and sometimes triggers bug 1. ++ * 3. mmap() is not supported (requires BM-DMA - see bug 4) ++ * 4. Really, we want to do scatter BM-DMA. Is the CyberPro capable of this? ++ * The Cyberpro seems to randomly scribble to various PCI addresses if you ++ * transfer >16 words. ++ * 5. We shouldn't ignore O_NONBLOCK when reading a frame. ++ * 6. The incoming stream on the NetWinder is CCIR656, which is YUV422. ++ * CyberPro docs also call the format we capture and overlay "YUV422", ++ * but we actually seem to have Y, U, Y, V bytes (is this YUYV format?) ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Russell King "); ++MODULE_DESCRIPTION("CyberPro v4l video grabber"); ++MODULE_LICENSE("GPL"); ++ ++#include "../../video/cyber2000fb.h" ++ ++/* ++ * These enable various experimental features. Most of these ++ * are just plain broken or just don't work at the moment. ++ */ ++/* ++ * Enable this if you want mmap() access. (see bug 4) ++ */ ++#undef USE_MMAP ++ ++/* ++ * Enable this if you want mmio access. (slow) ++ */ ++#define USE_MMIO ++ ++/* ++ * The V4L API is unclear whether VIDIOCSCAPTURE call is allowed while ++ * capture is running. The default is to disallow the call. ++ * ++ * Define this if you do want to allow the call while capture is active. ++ */ ++#undef ALLOW_SCAPTURE_WHILE_CAP ++ ++/* ++ * We capture two frames ++ */ ++#define NR_FRAMES 2 ++ ++/* ++ * One frame of video is 202 pages, assuming YUV422 format, 716x576 ++ */ ++#define NR_PAGES 202 ++ ++struct src_info { ++ unsigned int offset; /* offset of source data */ ++ unsigned int x; /* source x */ ++ unsigned int y; /* source y */ ++ unsigned int width; /* source width */ ++ unsigned int height; /* source height */ ++ unsigned int format; /* source format */ ++}; ++ ++struct dst_info { ++ unsigned int x; /* destination x */ ++ unsigned int y; /* destination y */ ++ unsigned int width; /* destination width */ ++ unsigned int height; /* destination height */ ++ unsigned int chromakey; /* chromakey */ ++ unsigned int flags; /* flags (eg, chromakey enable) */ ++}; ++ ++struct cyberpro_vidinfo; ++ ++struct win_info { ++ void (*init)(struct cyberpro_vidinfo *dp, struct win_info *wi); ++ void (*set_src)(struct cyberpro_vidinfo *dp, struct win_info *wi); ++ void (*set_win)(struct cyberpro_vidinfo *dp, struct win_info *wi); ++ void (*ctl)(struct cyberpro_vidinfo *dp, struct win_info *wi, int on_off); ++ ++ /* public */ ++ struct src_info src; ++ struct dst_info dst; ++ ++ /* private */ ++ unsigned short vid_fifo_ctl; ++ unsigned char vid_fmt; ++ unsigned char vid_disp_ctl1; ++ unsigned char vid_fifo_ctl1; ++ unsigned char vid_misc_ctl1; ++}; ++ ++struct framebuf { ++ unsigned int offset; /* mmap offset for this frame */ ++ unsigned int status; ++#define FRAME_FREE 0 ++#define FRAME_DONE 1 ++#define FRAME_WAITING 2 ++#define FRAME_GRABBING 3 ++ ++ /* ++ * Bus-Master DMA stuff. Note that we should ++ * probably use the kiovec stuff instead. ++ */ ++ unsigned long bus_addr[NR_PAGES]; /* list of pages */ ++ struct page *pages[NR_PAGES]; ++ void *buffer; ++ int dbg; ++}; ++ ++struct cyberpro_vidinfo { ++ struct video_device *dev; ++ struct i2c_bus *bus; ++ struct cyberpro_info info; /* host information */ ++ unsigned char *regs; ++ unsigned int irq; /* PCI interrupt number */ ++ ++ /* hardware configuration */ ++ unsigned int stream_fmt; /* format of stream from decoder*/ ++ ++ /* software settings */ ++ unsigned int decoder:1; /* decoder loaded */ ++ unsigned int interlace:1; /* interlace */ ++ unsigned int buf_set:1; /* VIDIOCSFBUF has been issued */ ++ unsigned int win_set:1; /* VIDIOCSWIN has been issued */ ++ unsigned int cap_active:1; /* capture is active */ ++ unsigned int ovl_active:1; /* overlay is active */ ++ unsigned int mmaped:1; /* buffer is mmap()d */ ++ unsigned int unused:25; ++ ++ unsigned int users; /* number of users */ ++ unsigned long cap_mem_offset; /* capture framebuffer offset */ ++ void * buffer; /* kernel capture buffer */ ++ unsigned int norm; /* video standard */ ++ ++ struct video_capability cap; /* capabilities */ ++ struct video_picture pic; /* current picture settings */ ++ struct video_buffer buf; /* display parameters */ ++ struct video_capture capt; /* video capture params */ ++ ++ struct win_info *ovl; /* overlay window set */ ++ struct win_info ext; /* "Extended" window info */ ++ struct win_info v2; /* "V2" window info */ ++ struct win_info x2; /* "X2" window info */ ++ ++ unsigned int bm_offset; /* Cap memory bus master offset */ ++ unsigned int bm_index; /* Cap page index */ ++ ++#ifdef USE_MMAP ++ unsigned int frame_idx; /* currently grabbing frame */ ++ unsigned int frame_size; ++ struct framebuf frame[NR_FRAMES]; ++ wait_queue_head_t frame_wait; ++#endif ++ ++ wait_queue_head_t vbl_wait; ++ ++ /* ++ * cyberpro registers ++ */ ++ unsigned char cap_mode1; ++ unsigned char cap_mode2; ++ unsigned char cap_miscctl; ++ unsigned char vfac1; ++ unsigned char vfac3; ++}; ++ ++/* ++ * Our access methods. ++ */ ++#define cyberpro_writel(val,reg,dp) writel(val, (dp)->regs + (reg)) ++#define cyberpro_writew(val,reg,dp) writew(val, (dp)->regs + (reg)) ++#define cyberpro_writeb(val,reg,dp) writeb(val, (dp)->regs + (reg)) ++ ++#define cyberpro_readb(reg,dp) readb((dp)->regs + (reg)) ++ ++static inline void ++cyberpro_grphw(unsigned int reg, unsigned int val, struct cyberpro_vidinfo *dp) ++{ ++ cyberpro_writew((reg & 255) | val << 8, 0x3ce, dp); ++} ++ ++static void cyberpro_grphw8(unsigned int reg, unsigned int val, struct cyberpro_vidinfo *dp) ++{ ++ cyberpro_grphw(reg, val, dp); ++} ++ ++static unsigned char cyberpro_grphr8(int reg, struct cyberpro_vidinfo *dp) ++{ ++ cyberpro_writeb(reg, 0x3ce, dp); ++ return cyberpro_readb(0x3cf, dp); ++} ++ ++static void cyberpro_grphw16(int reg, unsigned int val, struct cyberpro_vidinfo *dp) ++{ ++ cyberpro_grphw(reg, val, dp); ++ cyberpro_grphw(reg + 1, val >> 8, dp); ++} ++ ++static void cyberpro_grphw24(int reg, unsigned int val, struct cyberpro_vidinfo *dp) ++{ ++ cyberpro_grphw(reg, val, dp); ++ cyberpro_grphw(reg + 1, val >> 8, dp); ++ cyberpro_grphw(reg + 2, val >> 16, dp); ++} ++ ++#if 0 ++static void ++cyberpro_dbg_dump(void) ++{ ++ int i; ++ unsigned char idx[] = ++ { 0x30, 0x3e, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, ++ 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad }; ++ printk(KERN_DEBUG); ++ for (i = 0; i < sizeof(idx); i++) ++ printk("%02x ", idx[i]); ++ printk("\n" KERN_DEBUG); ++ for (i = 0; i < sizeof(idx); i++) ++ printk("%02x ", cyberpro_grphr8(idx[i])); ++ printk("\n"); ++} ++#endif ++ ++/* ++ * On the NetWinder, we can put the SAA7111 to sleep by holding ++ * it in reset. ++ * ++ * Note: once we have initialised the SAA7111, we can't put it back to ++ * sleep and expect it to keep its settings. Maybe a better solution ++ * is to register/de-register the i2c bus in open/release? ++ */ ++static void ++decoder_sleep(int sleep) ++{ ++#ifdef CONFIG_ARCH_NETWINDER ++ extern spinlock_t gpio_lock; ++ ++ spin_lock_irq(&gpio_lock); ++ cpld_modify(CPLD_7111_DISABLE, sleep ? CPLD_7111_DISABLE : 0); ++ spin_unlock_irq(&gpio_lock); ++ ++ if (!sleep) { ++ /* ++ * wait 20ms for device to wake up ++ */ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(HZ / 50); ++ } ++#endif ++} ++ ++/* -------------------------------- I2C support ---------------------------- */ ++ ++#define I2C_DELAY 100 ++ ++static void ++cyberpro_i2c_setlines(struct i2c_bus *bus, int ctrl, int data) ++{ ++ struct cyberpro_vidinfo *dp = bus->data; ++ int v; ++ ++ v = (ctrl ? EXT_LATCH2_I2C_CLKEN : 0x00) | (data ? EXT_LATCH2_I2C_DATEN : 0x00); ++ cyberpro_grphw8(EXT_LATCH2, v, dp); ++ ++ udelay(I2C_DELAY); ++} ++ ++static int ++cyberpro_i2c_getdataline(struct i2c_bus *bus) ++{ ++ struct cyberpro_vidinfo *dp = bus->data; ++ unsigned long flags; ++ int v; ++ ++ save_flags(flags); ++ cli(); ++ ++ v = cyberpro_grphr8(EXT_LATCH2, dp); ++ ++ restore_flags(flags); ++ ++ return v & EXT_LATCH2_I2C_DAT ? 1 : 0; ++} ++ ++static void ++cyberpro_i2c_attach(struct i2c_bus *bus, int id) ++{ ++ struct cyberpro_vidinfo *dp = bus->data; ++ int zero = 0; ++ ++ if (id == I2C_DRIVERID_VIDEODECODER) { ++ __u16 norm = dp->norm; ++ i2c_control_device(bus, id, DECODER_SET_NORM, &norm); ++ i2c_control_device(bus, id, DECODER_SET_PICTURE, &dp->pic); ++ i2c_control_device(bus, id, DECODER_ENABLE_OUTPUT, &zero); ++ ++ dp->decoder = 1; ++ } ++} ++ ++static void ++cyberpro_i2c_detach(struct i2c_bus *bus, int id) ++{ ++ struct cyberpro_vidinfo *dp = bus->data; ++ ++ if (id == I2C_DRIVERID_VIDEODECODER) ++ dp->decoder = 0; ++} ++ ++static struct i2c_bus cyberpro_i2c_bus = { ++ name: "", ++ id: I2C_BUSID_CYBER2000, ++ bus_lock: SPIN_LOCK_UNLOCKED, ++ attach_inform: cyberpro_i2c_attach, ++ detach_inform: cyberpro_i2c_detach, ++ i2c_setlines: cyberpro_i2c_setlines, ++ i2c_getdataline: cyberpro_i2c_getdataline, ++}; ++ ++/*------------------------- Extended Overlay Window ------------------------- ++ * Initialise 1st overlay window (works) ++ */ ++static void ++cyberpro_ext_init(struct cyberpro_vidinfo *dp, struct win_info *wi) ++{ ++ wi->vid_fifo_ctl = 0xf87c; ++ wi->vid_fmt = EXT_VID_FMT_YUV422; ++ wi->vid_disp_ctl1 = EXT_VID_DISP_CTL1_VINTERPOL_OFF | ++ EXT_VID_DISP_CTL1_NOCLIP; ++ wi->vid_fifo_ctl1 = EXT_VID_FIFO_CTL1_INTERLEAVE | ++ EXT_VID_FIFO_CTL1_OE_HIGH; ++ wi->vid_misc_ctl1 = 0; ++ ++ cyberpro_grphw8 (EXT_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); ++ cyberpro_grphw16(EXT_DDA_X_INIT, 0x0800, dp); ++ cyberpro_grphw16(EXT_DDA_Y_INIT, 0x0800, dp); ++ cyberpro_grphw16(EXT_VID_FIFO_CTL, wi->vid_fifo_ctl, dp); ++ cyberpro_grphw8 (EXT_VID_FIFO_CTL1, wi->vid_fifo_ctl1, dp); ++} ++ ++/* ++ * Set the source parameters for the extended window ++ */ ++static void ++cyberpro_ext_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi) ++{ ++ unsigned int phase, pitch; ++ ++ pitch = (wi->src.width >> 2) & 0x0fff; ++ phase = (wi->src.width + 3) >> 2; ++ ++ wi->vid_fmt &= ~7; ++ switch (wi->src.format) { ++ case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565; break; ++ case VIDEO_PALETTE_RGB24: wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break; ++ case VIDEO_PALETTE_RGB32: wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break; ++ case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555; break; ++ case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422; break; ++ } ++ ++ cyberpro_grphw24(EXT_MEM_START, wi->src.offset, dp); ++ cyberpro_grphw16(EXT_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp); ++ cyberpro_grphw8 (EXT_SRC_WIN_WIDTH, phase, dp); ++ cyberpro_grphw8 (EXT_VID_FMT, wi->vid_fmt, dp); ++} ++ ++/* ++ * Set overlay1 window ++ */ ++static void ++cyberpro_ext_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi) ++{ ++ unsigned int xscale, yscale; ++ unsigned int xoff, yoff; ++ ++ /* ++ * Note: the offset does not appear to be influenced by ++ * hardware scrolling. ++ */ ++ xoff = yoff = 0; ++ ++ xoff += wi->dst.x; ++ yoff += wi->dst.y; ++ ++ xscale = wi->src.width; ++ ++ if (wi->dst.width >= wi->src.width * 2) { ++ wi->vid_fmt |= EXT_VID_FMT_DBL_H_PIX; ++ xscale *= 2; ++ } else { ++ wi->vid_fmt &= ~EXT_VID_FMT_DBL_H_PIX; ++ } ++ ++ xscale = ((xscale - /*2*/0) * 4096) / wi->dst.width; ++ yscale = ((wi->src.height - /*2*/0) * 4096) / wi->dst.height; ++ ++ cyberpro_grphw16(EXT_X_START, xoff, dp); ++ cyberpro_grphw16(EXT_X_END, xoff + wi->dst.width, dp); ++ cyberpro_grphw16(EXT_Y_START, yoff, dp); ++ cyberpro_grphw16(EXT_Y_END, yoff + wi->dst.height, dp); ++ cyberpro_grphw24(EXT_COLOUR_COMPARE, wi->dst.chromakey, dp); ++ cyberpro_grphw16(EXT_DDA_X_INC, xscale, dp); ++ cyberpro_grphw16(EXT_DDA_Y_INC, yscale, dp); ++ cyberpro_grphw8(EXT_VID_FMT, wi->vid_fmt, dp); ++ ++ if (wi->dst.flags & VIDEO_WINDOW_CHROMAKEY) ++ wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_IGNORE_CCOMP; ++ else ++ wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_IGNORE_CCOMP; ++} ++ ++/* ++ * Enable or disable the 1st overlay window. Note that for anything ++ * useful to be displayed, we must have capture enabled. ++ */ ++static void ++cyberpro_ext_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on) ++{ ++ if (on) ++ wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW; ++ else ++ wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW; ++ ++ cyberpro_grphw8(EXT_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); ++} ++ ++/*------------------------------- V2 Overlay Window ------------------------- ++ * Initialise 2nd overlay window (guesswork) ++ */ ++static void ++cyberpro_v2_init(struct cyberpro_vidinfo *dp, struct win_info *wi) ++{ ++ wi->vid_fifo_ctl = 0xf87c; ++ wi->vid_fmt = EXT_VID_FMT_YUV422; ++ wi->vid_disp_ctl1 = EXT_VID_DISP_CTL1_VINTERPOL_OFF | ++ EXT_VID_DISP_CTL1_NOCLIP; ++ wi->vid_fifo_ctl1 = 0x06; ++ wi->vid_misc_ctl1 = 0; ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); ++ cyberpro_grphw8 (Y_V2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); ++ /* No DDA init values */ ++ cyberpro_grphw16(Y_V2_VID_FIFO_CTL, wi->vid_fifo_ctl, dp); ++ cyberpro_grphw8 (Y_V2_VID_FIFO_CTL1, wi->vid_fifo_ctl1, dp); ++} ++ ++/* ++ * Set the source parameters for the v2 window ++ */ ++static void ++cyberpro_v2_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi) ++{ ++ unsigned int phase, pitch; ++ ++ pitch = (wi->src.width >> 2) & 0x0fff; ++ phase = (wi->src.width + 3) >> 2; ++ ++ wi->vid_fmt &= ~7; ++ switch (wi->src.format) { ++ case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565; break; ++ case VIDEO_PALETTE_RGB24: wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break; ++ case VIDEO_PALETTE_RGB32: wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break; ++ case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555; break; ++ case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422; break; ++ } ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_X, dp); ++ cyberpro_grphw24(X_V2_VID_MEM_START, wi->src.offset, dp); ++ cyberpro_grphw16(X_V2_VID_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp); ++ cyberpro_grphw8 (X_V2_VID_SRC_WIN_WIDTH, phase, dp); ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); ++ cyberpro_grphw8(Y_V2_VID_FMT, wi->vid_fmt, dp); ++} ++ ++/* ++ * Set v2 window ++ */ ++static void ++cyberpro_v2_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi) ++{ ++ unsigned int xscale, yscale; ++ unsigned int xoff, yoff; ++ ++ /* ++ * Note: the offset does not appear to be influenced by ++ * hardware scrolling. ++ */ ++ xoff = yoff = 0; ++ ++ xoff += wi->dst.x; ++ yoff += wi->dst.y; ++ ++ xscale = (wi->src.width * 4096) / wi->dst.width; ++ yscale = (wi->src.height * 4096) / wi->dst.height; ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_X, dp); ++ cyberpro_grphw16(X_V2_X_START, xoff, dp); ++ cyberpro_grphw16(X_V2_X_END, xoff + wi->dst.width, dp); ++ cyberpro_grphw16(X_V2_Y_START, yoff, dp); ++ cyberpro_grphw16(X_V2_Y_END, yoff + wi->dst.height, dp); ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); ++ cyberpro_grphw16(Y_V2_DDA_X_INC, xscale, dp); ++ cyberpro_grphw16(Y_V2_DDA_Y_INC, yscale, dp); ++} ++ ++/* ++ * Enable or disable the 2nd overlay window. Note that for anything ++ * useful to be displayed, we must have capture enabled. ++ */ ++static void ++cyberpro_v2_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on) ++{ ++ if (on) ++ wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW; ++ else ++ wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW; ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); ++ cyberpro_grphw8(Y_V2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); ++} ++ ++/*--------------------------- X2 Overlay Window ----------------------------- ++ * Initialise 3rd overlay window (guesswork) ++ */ ++static void ++cyberpro_x2_init(struct cyberpro_vidinfo *dp, struct win_info *wi) ++{ ++ wi->vid_fmt = EXT_VID_FMT_YUV422; ++ wi->vid_disp_ctl1 = 0x40; ++ wi->vid_misc_ctl1 = 0; ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_K, dp); ++ cyberpro_grphw8 (K_X2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); ++ cyberpro_grphw16(K_X2_DDA_X_INIT, 0x0800, dp); ++ cyberpro_grphw16(K_X2_DDA_Y_INIT, 0x0800, dp); ++} ++ ++/* ++ * Set the source parameters for the x2 window ++ */ ++static void ++cyberpro_x2_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi) ++{ ++ unsigned int phase, pitch; ++ ++ pitch = (wi->src.width >> 2) & 0x0fff; ++ phase = (wi->src.width + 3) >> 2; ++ ++ wi->vid_fmt &= ~7; ++ switch (wi->src.format) { ++ case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565; break; ++ case VIDEO_PALETTE_RGB24: wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break; ++ case VIDEO_PALETTE_RGB32: wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break; ++ case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555; break; ++ case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422; break; ++ } ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_J, dp); ++ cyberpro_grphw24(J_X2_VID_MEM_START, wi->src.offset, dp); ++ cyberpro_grphw16(J_X2_VID_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp); ++ cyberpro_grphw8 (J_X2_VID_SRC_WIN_WIDTH, phase, dp); ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_K, dp); ++ cyberpro_grphw8(K_X2_VID_FMT, wi->vid_fmt, dp); ++} ++ ++/* ++ * Set x2 window ++ */ ++static void ++cyberpro_x2_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi) ++{ ++ unsigned int xscale, yscale; ++ unsigned int xoff, yoff; ++ ++ /* ++ * Note: the offset does not appear to be influenced by ++ * hardware scrolling. ++ */ ++ xoff = yoff = 0; ++ ++ xoff += wi->dst.x; ++ yoff += wi->dst.y; ++ ++ xscale = (wi->src.width * 4096) / wi->dst.width; ++ yscale = (wi->src.height * 4096) / wi->dst.height; ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_J, dp); ++ cyberpro_grphw16(J_X2_X_START, xoff, dp); ++ cyberpro_grphw16(J_X2_X_END, xoff + wi->dst.width, dp); ++ cyberpro_grphw16(J_X2_Y_START, yoff, dp); ++ cyberpro_grphw16(J_X2_Y_END, yoff + wi->dst.height, dp); ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_K, dp); ++ cyberpro_grphw16(K_X2_DDA_X_INC, xscale, dp); ++ cyberpro_grphw16(K_X2_DDA_Y_INC, yscale, dp); ++} ++ ++/* ++ * Enable or disable the 3rd overlay window. Note that for anything ++ * useful to be displayed, we must have capture enabled. ++ */ ++static void ++cyberpro_x2_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on) ++{ ++ if (on) ++ wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW; ++ else ++ wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW; ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_K, dp); ++ cyberpro_grphw8(K_X2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); ++} ++ ++/* ------------------------------------------------------------------------- */ ++ ++#if 0 ++static void reset_seq(struct cyberpro_vidinfo *dp) ++{ ++ unsigned char ext_mem_ctl = cyberpro_grphr8(0x70, dp); ++ ++ cyberpro_grphw8(ext_mem_ctl | 0x80, 0x70, dp); ++ cyberpro_grphw8(ext_mem_ctl, 0x70, dp); ++} ++#endif ++ ++#ifdef USE_MMAP ++/* ++ * Buffer support ++ */ ++static int ++cyberpro_alloc_frame_buffer(struct cyberpro_vidinfo *dp, ++ struct framebuf *frame) ++{ ++ unsigned long addr; ++ void *buffer; ++ int pgidx; ++ ++ if (frame->buffer) ++ return 0; ++ ++ /* ++ * Allocate frame buffer ++ */ ++ buffer = vmalloc(NR_PAGES * PAGE_SIZE); ++ ++ if (frame->buffer) { ++ vfree(buffer); ++ return 0; ++ } ++ ++ if (!buffer) ++ return -ENOMEM; ++ ++ printk("Buffer allocated @ %p [", buffer); ++ ++ frame->buffer = buffer; ++ frame->dbg = 1; ++ ++ /* ++ * Don't leak information from the kernel. ++ */ ++ memset(buffer, 0x5a, NR_PAGES * PAGE_SIZE); ++ ++ /* ++ * Now, reserve all the pages, and calculate ++ * each pages' bus address. ++ */ ++ addr = (unsigned long)buffer; ++ for (pgidx = 0; pgidx < NR_PAGES; pgidx++, addr += PAGE_SIZE) { ++ struct page *page; ++ pgd_t *pgd; ++ pmd_t *pmd; ++ pte_t *pte; ++ ++ /* ++ * The page should be present. If not, ++ * vmalloc has gone nuts. ++ */ ++ pgd = pgd_offset_k(addr); ++ if (pgd_none(*pgd)) ++ BUG(); ++ pmd = pmd_offset(pgd, addr); ++ if (pmd_none(*pmd)) ++ BUG(); ++ pte = pte_offset(pmd, addr); ++ if (!pte_present(*pte)) ++ BUG(); ++ ++ page = pte_page(*pte); ++ ++ frame->bus_addr[pgidx] = virt_to_bus((void *)page_address(page)); ++ frame->pages[pgidx] = page; ++ SetPageReserved(page); ++ ++ printk("%08lx (%08lx) ", page_address(page), frame->bus_addr[pgidx]); ++ } ++ printk("\n"); ++ ++ return 0; ++} ++ ++static void ++cyberpro_frames_free_one(struct cyberpro_vidinfo *dp, struct framebuf *frame) ++{ ++ void *buffer; ++ int pgidx; ++ ++ frame->status = FRAME_FREE; ++ buffer = frame->buffer; ++ frame->buffer = NULL; ++ ++ if (buffer) { ++ for (pgidx = 0; pgidx < NR_PAGES; pgidx++) { ++ frame->bus_addr[pgidx] = 0; ++ ClearPageReserved(frame->pages[pgidx]); ++ frame->pages[pgidx] = NULL; ++ } ++ vfree(buffer); ++ } ++} ++ ++static void ++cyberpro_busmaster_frame(struct cyberpro_vidinfo *dp, struct framebuf *frame) ++{ ++ unsigned long bus_addr; ++ ++ bus_addr = frame->bus_addr[dp->bm_index]; ++ ++ if (frame->dbg) { ++ printk("Frame%d: %06x -> %08lx\n", ++ dp->frame_idx, ++ dp->bm_offset, ++ bus_addr); ++ } ++ ++ cyber2000_outw(dp->bm_offset, BM_VID_ADDR_LOW); ++ cyber2000_outw(dp->bm_offset >> 16, BM_VID_ADDR_HIGH); ++ ++ cyber2000_outw(bus_addr, BM_ADDRESS_LOW); ++ cyber2000_outw(bus_addr >> 16, BM_ADDRESS_HIGH); ++ ++ /* ++ * One page-full only ++ */ ++ cyber2000_outw(1023, BM_LENGTH); ++ ++ /* ++ * Load length ++ */ ++ cyber2000_outw(BM_CONTROL_INIT, BM_CONTROL); ++ ++ /* ++ * Enable transfer ++ */ ++ cyber2000_outw(BM_CONTROL_ENABLE|BM_CONTROL_IRQEN, BM_CONTROL); ++ ++ dp->bm_offset += 1024; ++ dp->bm_index += 1; ++} ++ ++static void cyberpro_busmaster_interrupt(struct cyberpro_vidinfo *dp) ++{ ++ struct framebuf *frame = dp->frame + dp->frame_idx; ++ ++ /* ++ * Disable Busmaster operations ++ */ ++ cyber2000_outw(0, BM_CONTROL); ++ ++ if (frame->status == FRAME_GRABBING) { ++ /* ++ * We are still grabbing this frame to system ++ * memory. Transfer next page if there are ++ * more, or else flag this frame as complete. ++ */ ++ if (dp->bm_index < NR_PAGES) ++ cyberpro_busmaster_frame(dp); ++ else { ++ unsigned int idx; ++ ++ frame->status = FRAME_DONE; ++ frame->dbg = 0; ++ ++ idx = dp->frame_idx + 1; ++ if (idx >= NR_FRAMES) ++ idx = 0; ++ ++ dp->frame_idx = idx; ++ ++ wake_up(&dp->frame_wait); ++ } ++ } ++} ++ ++static void cyberpro_frames_vbl(struct cyberpro_vidinfo *dp, unsigned int stat) ++{ ++ struct framebuf *frame = dp->frame + dp->frame_idx; ++ ++ /* ++ * No point capturing frames if the grabber isn't active. ++ */ ++ if (stat & EXT_ROM_UCB4GH_FREEZE) ++ return; ++ ++ /* ++ * If the next buffer is ready for grabbing, ++ * set up the bus master registers for the ++ * transfer. ++ */ ++ if (frame->status == FRAME_WAITING) { ++ frame->status = FRAME_GRABBING; ++ ++ dp->bm_offset = dp->cap_mem_offset; ++ dp->bm_index = 0; ++ ++ cyberpro_busmaster_frame(dp, frame); ++ } ++} ++ ++static void __init cyberpro_frames_init(struct cyberpro_vidinfo *dp) ++{ ++ unsigned int offset, maxsize; ++ int i; ++ ++ init_waitqueue_head(&dp->frame_wait); ++ ++ maxsize = 2 * dp->cap.maxwidth * dp->cap.maxheight; ++ dp->frame_size = PAGE_ALIGN(maxsize); ++ dp->frame_idx = 0; ++ ++ for (i = offset = 0; i < NR_FRAMES; i++) { ++ dp->frame[i].offset = offset; ++ dp->frame[i].status = FRAME_FREE; ++ offset += dp->frame_size; ++ } ++} ++ ++static void cyberpro_frames_free(struct cyberpro_vidinfo *dp) ++{ ++ int i; ++ ++ dp->mmaped = 0; ++ ++ /* ++ * Free all frame buffers ++ */ ++ for (i = 0; i < NR_FRAMES; i++) ++ cyberpro_frames_free_one(dp, dp->frame + i); ++} ++ ++#else ++#define cyberpro_frames_vbl(dp,stat) do { } while (0) ++#define cyberpro_frames_init(dp) do { } while (0) ++#define cyberpro_frames_free(dp) do { } while (0) ++#endif ++ ++/* ++ * CyberPro Interrupts ++ * ------------------- ++ * ++ * We don't really know how to signal an IRQ clear to the chip. However, ++ * disabling and re-enabling the capture interrupt enable seems to do what ++ * we want. ++ */ ++static void cyberpro_interrupt(int nr, void *dev_id, struct pt_regs *regs) ++{ ++ struct cyberpro_vidinfo *dp = dev_id; ++ unsigned char old_grphidx; ++ unsigned int status; ++ ++ /* ++ * Save old graphics index register ++ */ ++ old_grphidx = cyberpro_readb(0x3ce, dp); ++ ++ status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); ++ ++ /* ++ * Was it due to the Capture VSYNC? ++ */ ++ if (status & EXT_ROM_UCB4GH_INTSTAT) { ++ /* ++ * Frob the IRQ enable bit to drop the request. ++ */ ++ cyberpro_grphw8(VFAC_CTL3, dp->vfac3 & ~VFAC_CTL3_CAP_IRQ, dp); ++ cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp); ++ ++ cyberpro_frames_vbl(dp, status); ++ wake_up(&dp->vbl_wait); ++ } ++ ++ /* ++ * Restore graphics controller index ++ */ ++ cyberpro_writeb(old_grphidx, 0x3ce, dp); ++ ++#ifdef USE_MMAP ++ /* ++ * Do Bus-Master IRQ stuff ++ */ ++ if (cyber2000_inb(BM_CONTROL) & (1 << 7)) ++ cyberpro_busmaster_interrupt(dp); ++#endif ++} ++ ++static void cyberpro_capture(struct cyberpro_vidinfo *dp, int on) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned int status; ++ ++ status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); ++ ++ add_wait_queue(&dp->vbl_wait, &wait); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ ++ if (!!on ^ !(status & EXT_ROM_UCB4GH_FREEZE)) { ++ if (on) { ++ schedule_timeout(40 * HZ / 1000); ++ dp->vfac1 &= ~(VFAC_CTL1_FREEZE_CAPTURE|VFAC_CTL1_FREEZE_CAPTURE_SYNC); ++ cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); ++ ++ status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); ++ } else { ++ dp->vfac1 |= VFAC_CTL1_FREEZE_CAPTURE_SYNC; ++ cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); ++ ++ status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); ++ if (!(status & EXT_ROM_UCB4GH_FREEZE)) ++ schedule_timeout(40 * HZ / 1000); ++ } ++ } ++ ++ current->state = TASK_RUNNING; ++ remove_wait_queue(&dp->vbl_wait, &wait); ++} ++ ++static void cyberpro_capture_one(struct cyberpro_vidinfo *dp) ++{ ++ struct task_struct *tsk = current; ++ DECLARE_WAITQUEUE(wait, tsk); ++ unsigned int status; ++ unsigned long policy, rt_priority; ++ ++ policy = tsk->policy; ++ rt_priority = tsk->rt_priority; ++ ++ tsk->policy = SCHED_FIFO; ++ tsk->rt_priority = 1; ++ ++ status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); ++ ++ add_wait_queue(&dp->vbl_wait, &wait); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ ++ schedule_timeout(40 * HZ / 1000); ++ dp->vfac1 &= ~(VFAC_CTL1_FREEZE_CAPTURE|VFAC_CTL1_FREEZE_CAPTURE_SYNC); ++ cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); ++ ++ status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(40 * HZ / 1000); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(40 * HZ / 1000); ++ ++ dp->vfac1 |= VFAC_CTL1_FREEZE_CAPTURE_SYNC; ++ cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); ++ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); ++ ++ current->state = TASK_RUNNING; ++ remove_wait_queue(&dp->vbl_wait, &wait); ++ ++ tsk->policy = policy; ++ tsk->rt_priority = rt_priority; ++} ++ ++static void cyberpro_capture_set_win(struct cyberpro_vidinfo *dp) ++{ ++ unsigned int xstart, xend, ystart, yend; ++ ++ xstart = 4 + dp->capt.x; ++ xend = xstart + dp->capt.width; ++ ++ if (dp->cap_mode1 & EXT_CAP_MODE1_8BIT) { ++ /* 8-bit capture */ ++ xstart *= 2; ++ xend *= 2; ++ } ++ ++ xstart -= 1; ++ xend -= 1; ++ ++ ystart = 18 + dp->capt.y; ++ yend = ystart + dp->capt.height / 2; ++ ++ cyberpro_grphw16(CAP_X_START, xstart, dp); ++ cyberpro_grphw16(CAP_X_END, xend + 1, dp); ++ cyberpro_grphw16(CAP_Y_START, ystart, dp); ++ cyberpro_grphw16(CAP_Y_END, yend + 2, dp); ++ ++ /* ++ * This should take account of capt.decimation ++ */ ++ cyberpro_grphw16(CAP_DDA_X_INIT, 0x0800, dp); ++ cyberpro_grphw16(CAP_DDA_X_INC, 0x1000, dp); ++ cyberpro_grphw16(CAP_DDA_Y_INIT, 0x0800, dp); ++ cyberpro_grphw16(CAP_DDA_Y_INC, 0x1000, dp); ++ ++ cyberpro_grphw8(CAP_PITCH, dp->capt.width >> 2, dp); ++} ++ ++static void cyberpro_set_interlace(struct cyberpro_vidinfo *dp) ++{ ++ /* ++ * set interlace mode ++ */ ++ if (dp->interlace) { ++ dp->vfac3 |= VFAC_CTL3_CAP_INTERLACE; ++ dp->cap_miscctl &= ~CAP_CTL_MISC_ODDEVEN; ++ dp->ovl->src.height = dp->capt.height; ++ } else { ++ dp->vfac3 &= ~VFAC_CTL3_CAP_INTERLACE; ++ dp->cap_miscctl |= CAP_CTL_MISC_ODDEVEN; ++ dp->ovl->src.height = dp->capt.height / 2; ++ } ++ ++ cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp); ++ cyberpro_grphw8(CAP_CTL_MISC, dp->cap_miscctl, dp); ++ ++ dp->ovl->set_src(dp, dp->ovl); ++ ++ if (dp->win_set) ++ dp->ovl->set_win(dp, dp->ovl); ++} ++ ++/* ++ * Calculate and set the address of the capture buffer. Note we ++ * also update the extended memory buffer for the overlay window. ++ * ++ * base: phys base address of display ++ * width: pixel width of display ++ * height: height of display ++ * depth: depth of display (8/16/24) ++ * bytesperline: number of bytes on a line ++ * ++ * We place the capture buffer 16K after the screen. ++ */ ++static int ++cyberpro_set_buffer(struct cyberpro_vidinfo *dp, struct video_buffer *b) ++{ ++ unsigned long screensize, maxbufsz; ++ ++ if (b->height <= 0 || b->width <= 0 || b->bytesperline <= 0) ++ return -EINVAL; ++ ++ maxbufsz = dp->cap.maxwidth * dp->cap.maxheight * 2; ++ screensize = b->height * b->bytesperline + 16384; ++ ++ if ((screensize + maxbufsz) >= dp->info.fb_size) ++ return -EINVAL; ++ ++ dp->buf.base = b->base; ++ dp->buf.width = b->width; ++ dp->buf.height = b->height; ++ dp->buf.depth = b->depth; ++ dp->buf.bytesperline = b->bytesperline; ++ dp->cap_mem_offset = screensize >> 2; ++ ++ cyberpro_grphw24(CAP_MEM_START, dp->cap_mem_offset, dp); ++ ++ /* ++ * Setup the overlay source information. ++ */ ++ dp->ovl->src.offset = dp->cap_mem_offset; ++ dp->ovl->set_src(dp, dp->ovl); ++ ++ return 0; ++} ++ ++static void cyberpro_hw_init(struct cyberpro_vidinfo *dp) ++{ ++ unsigned char old; ++ ++ /* ++ * Enable access to bus-master registers ++ */ ++ dp->info.enable_extregs(dp->info.info); ++ ++ dp->vfac1 = VFAC_CTL1_PHILIPS | ++ VFAC_CTL1_FREEZE_CAPTURE | ++ VFAC_CTL1_FREEZE_CAPTURE_SYNC; ++ dp->vfac3 = VFAC_CTL3_CAP_IRQ; ++ ++ dp->cap_miscctl = CAP_CTL_MISC_DISPUSED | ++ CAP_CTL_MISC_SYNCTZOR | ++ CAP_CTL_MISC_SYNCTZHIGH; ++ ++ /* ++ * Setup bus-master mode ++ */ ++ cyberpro_grphw8(BM_CTRL1, 0x88, dp); ++ cyberpro_grphw8(PCI_BM_CTL, PCI_BM_CTL_ENABLE, dp); ++ cyberpro_grphw8(BM_CTRL0, 0x44, dp); ++ cyberpro_grphw8(BM_CTRL1, 0x84, dp); ++ ++ cyberpro_grphw24(CAP_MEM_START, 0, dp); ++ ++ cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); ++ cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp); ++ cyberpro_grphw8(VFAC_CTL2, 0, dp); ++ ++ cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); ++ cyberpro_grphw8(EXT_TV_CTL, 0x80, dp); ++ ++ cyberpro_grphw8(EXT_CAP_CTL1, 0x3f, dp); /* disable PIP */ ++ cyberpro_grphw8(EXT_CAP_CTL2, 0xc0 | EXT_CAP_CTL2_ODDFRAMEIRQ, dp); ++ ++ /* ++ * Configure capture mode to match the ++ * external video processor format ++ */ ++ cyberpro_grphw8(EXT_CAP_MODE1, dp->cap_mode1, dp); ++ cyberpro_grphw8(EXT_CAP_MODE2, dp->cap_mode2, dp); ++ ++ /* setup overlay */ ++ cyberpro_grphw16(EXT_FIFO_CTL, 0x1010, dp); ++// cyberpro_grphw16(EXT_FIFO_CTL, 0x1b0f, dp); ++ ++ /* ++ * Always reset the capture parameters on each open. ++ */ ++ dp->capt.x = 0; ++ dp->capt.y = 0; ++ dp->capt.width = dp->cap.maxwidth; ++ dp->capt.height = dp->cap.maxheight; ++ dp->capt.decimation = 0; ++ dp->capt.flags = 0; ++ ++ cyberpro_capture_set_win(dp); ++ ++ /* ++ * Enable VAFC ++ */ ++ old = cyberpro_grphr8(EXT_LATCH1, dp); ++ cyberpro_grphw8(EXT_LATCH1, old | EXT_LATCH1_VAFC_EN, dp); ++ ++ /* ++ * Enable capture (we hope that VSYNC=1) ++ */ ++ dp->vfac1 |= VFAC_CTL1_CAPTURE; ++ cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); ++ ++ /* ++ * The overlay source format is always the ++ * same as the capture stream format. ++ */ ++ dp->ovl->src.width = dp->capt.width; ++ dp->ovl->src.height = dp->capt.height; ++ dp->ovl->src.format = dp->stream_fmt; ++ ++ /* ++ * Initialise the overlay windows ++ */ ++ dp->ext.init(dp, &dp->ext); ++ dp->v2.init(dp, &dp->v2); ++ dp->x2.init(dp, &dp->x2); ++} ++ ++static void cyberpro_deinit(struct cyberpro_vidinfo *dp) ++{ ++ unsigned char old; ++ ++ /* ++ * Stop any bus-master activity ++ */ ++ cyberpro_writew(0, BM_CONTROL, dp); ++ ++ /* ++ * Shut down overlay ++ */ ++ if (dp->ovl_active) ++ dp->ovl->ctl(dp, dp->ovl, 0); ++ dp->ovl_active = 0; ++ ++ /* ++ * Shut down capture ++ */ ++ if (dp->cap_active) ++ cyberpro_capture(dp, 0); ++ dp->cap_active = 0; ++ ++ /* ++ * Disable all capture ++ */ ++ cyberpro_grphw8(VFAC_CTL1, 0, dp); ++ ++ /* ++ * Disable VAFC ++ */ ++ old = cyberpro_grphr8(EXT_LATCH1, dp); ++ cyberpro_grphw8(EXT_LATCH1, old & ~EXT_LATCH1_VAFC_EN, dp); ++ ++ /* ++ * Disable interrupt (this allows it to float) ++ */ ++ dp->vfac3 &= ~VFAC_CTL3_CAP_IRQ; ++ cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp); ++ ++ /* ++ * Switch off bus-master mode ++ */ ++ cyberpro_grphw8(PCI_BM_CTL, 0, dp); ++ ++ /* ++ * Disable access to bus-master registers ++ */ ++ dp->info.disable_extregs(dp->info.info); ++} ++ ++static int cyberpro_grabber_open(struct video_device *dev, int flags) ++{ ++ struct cyberpro_vidinfo *dp = dev->priv; ++ int ret, one = 1; ++ ++ MOD_INC_USE_COUNT; ++ ++ ret = -EBUSY; ++ if (flags || dp->users) ++ goto out; ++ ++ dp->users += 1; ++ ++ if (dp->users == 1) { ++ ret = request_irq(dp->irq, cyberpro_interrupt, SA_SHIRQ, ++ dp->info.dev_name, dp); ++ ++ if (ret) { ++ dp->users -= 1; ++ goto out; ++ } ++ ++ /* ++ * Initialise the VGA chip ++ */ ++ cyberpro_hw_init(dp); ++ ++ /* ++ * Enable the IRQ. This allows the IRQ to work as expected ++ * even if the IRQ line is missing the pull-up resistor. ++ */ ++ enable_irq(dp->irq); ++ ++ i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER, ++ DECODER_ENABLE_OUTPUT, &one); ++ } ++ ++ ret = 0; ++out: ++ if (ret) ++ MOD_DEC_USE_COUNT; ++ return ret; ++} ++ ++static void cyberpro_grabber_close(struct video_device *dev) ++{ ++ struct cyberpro_vidinfo *dp = dev->priv; ++ ++ if (dp->users == 1) { ++ int zero = 0; ++ ++ /* ++ * Disable the IRQ. This prevents problems with missing ++ * pull-up resistors on the PCI interrupt line. ++ */ ++ disable_irq(dp->irq); ++ ++ cyberpro_frames_free(dp); ++ ++ /* ++ * Turn off the SAA7111 decoder ++ */ ++ i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER, ++ DECODER_ENABLE_OUTPUT, &zero); ++ ++ /* ++ * Disable grabber ++ */ ++ cyberpro_deinit(dp); ++ ++ free_irq(dp->irq, dp); ++ } ++ ++ dp->users -= 1; ++ ++ MOD_DEC_USE_COUNT; ++} ++ ++/* ++ * Our general plan here is: ++ * 1. Set the CyberPro to perform a BM-DMA of one frame to this memory ++ * 2. Copy the frame to the userspace ++ * ++ * However, BM-DMA seems to be unreliable at the moment, especially on ++ * rev. 4 NetWinders. ++ */ ++static long ++cyberpro_grabber_read(struct video_device *dev, char *buf, ++ unsigned long count, int noblock) ++{ ++ struct cyberpro_vidinfo *dp = dev->priv; ++ int ret = -EINVAL; ++ ++#ifdef USE_MMIO ++ unsigned long maxbufsz = dp->capt.width * dp->capt.height * 2; ++ char *disp = dp->info.fb + (dp->cap_mem_offset << 2); ++ ++ /* ++ * If the buffer is mmap()'d, we shouldn't be using read() ++ */ ++ if (dp->mmaped) ++ return -EINVAL; ++ ++ if (count > maxbufsz) ++ count = maxbufsz; ++ ++ if (dp->cap_active) ++ cyberpro_capture(dp, 0); ++ else ++ cyberpro_capture_one(dp); ++ ++ ret = (int)count; ++ if (copy_to_user(buf, disp, count)) ++ ret = -EFAULT; ++ ++ /* ++ * unfreeze capture ++ */ ++ if (dp->cap_active) ++ cyberpro_capture(dp, 1); ++#endif ++ ++ return ret; ++} ++ ++/* ++ * We don't support writing to the grabber ++ * (In theory, we could allow writing to a separate region of VGA memory, ++ * and display this using the second overlay window. This would allow us ++ * to do video conferencing for example). ++ */ ++static long ++cyberpro_grabber_write(struct video_device *dev, const char *buf, ++ unsigned long count, int noblock) ++{ ++ return -EINVAL; ++} ++ ++static int ++cyberpro_grabber_ioctl(struct video_device *dev, unsigned int cmd, void *arg) ++{ ++ struct cyberpro_vidinfo *dp = dev->priv; ++ ++ switch (cmd) { ++ case VIDIOCGCAP: ++ return copy_to_user(arg, &dp->cap, sizeof(dp->cap)) ++ ? -EFAULT : 0; ++ ++ case VIDIOCGCHAN: ++ { ++ struct video_channel chan; ++ ++ chan.channel = 0; ++ strcpy(chan.name, "Composite"); ++ chan.tuners = 0; ++ chan.flags = 0; ++ chan.type = VIDEO_TYPE_CAMERA; ++ chan.norm = dp->norm; ++ ++ return copy_to_user(arg, &chan, sizeof(chan)) ? -EFAULT : 0; ++ } ++ ++ case VIDIOCGPICT: ++ return copy_to_user(arg, &dp->pic, sizeof(dp->pic)) ++ ? -EINVAL : 0; ++ ++ case VIDIOCGWIN: ++ { ++ struct video_window win; ++ ++ win.x = dp->ovl->dst.x; ++ win.y = dp->ovl->dst.y; ++ win.width = dp->ovl->dst.width; ++ win.height = dp->ovl->dst.height; ++ win.chromakey = dp->ovl->dst.chromakey; ++ win.flags = VIDEO_WINDOW_CHROMAKEY | ++ (dp->interlace ? VIDEO_WINDOW_INTERLACE : 0); ++ win.clips = NULL; ++ win.clipcount = 0; ++ ++ return copy_to_user(arg, &win, sizeof(win)) ++ ? -EINVAL : 0; ++ } ++ ++ case VIDIOCGFBUF: ++ return copy_to_user(arg, &dp->buf, sizeof(dp->buf)) ++ ? -EINVAL : 0; ++ ++ case VIDIOCGUNIT: ++ { ++ struct video_unit unit; ++ ++ unit.video = dev->minor; ++ unit.vbi = VIDEO_NO_UNIT; ++ unit.radio = VIDEO_NO_UNIT; ++ unit.audio = VIDEO_NO_UNIT; ++ unit.teletext = VIDEO_NO_UNIT; ++ ++ return copy_to_user(arg, &unit, sizeof(unit)) ++ ? -EINVAL : 0; ++ } ++ ++ case VIDIOCGCAPTURE: ++ return copy_to_user(arg, &dp->capt, sizeof(dp->capt)) ++ ? -EFAULT : 0; ++ ++ case VIDIOCSCHAN: ++ { ++ struct video_decoder_capability vdc; ++ struct video_channel v; ++ int ok; ++ ++ if (copy_from_user(&v, arg, sizeof(v))) ++ return -EFAULT; ++ ++ if (v.channel != 0) ++ return -EINVAL; ++ ++ i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER, ++ DECODER_GET_CAPABILITIES, &vdc); ++ ++ switch (v.norm) { ++ case VIDEO_MODE_PAL: ++ ok = vdc.flags & VIDEO_DECODER_PAL; ++ break; ++ case VIDEO_MODE_NTSC: ++ ok = vdc.flags & VIDEO_DECODER_NTSC; ++ break; ++ case VIDEO_MODE_AUTO: ++ ok = vdc.flags & VIDEO_DECODER_AUTO; ++ break; ++ default: ++ ok = 0; ++ } ++ if (!ok) ++ return -EINVAL; ++ ++ dp->norm = v.norm; ++ ++ i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER, ++ DECODER_SET_NORM, &v.norm); ++ ++ return 0; ++ } ++ ++ case VIDIOCSPICT: ++ { ++ struct video_picture p; ++ ++ if (copy_from_user(&p, arg, sizeof(p))) ++ return -EFAULT; ++ ++ if (p.palette != dp->stream_fmt || ++ p.depth != 8) ++ return -EINVAL; ++ ++ dp->pic = p; ++ ++ /* p.depth sets the capture depth */ ++ /* p.palette sets the capture palette */ ++ ++ i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER, ++ DECODER_SET_PICTURE, &p); ++ ++ return 0; ++ } ++ ++ case VIDIOCSWIN: /* set the size & position of the overlay window */ ++ { ++ struct video_window w; ++ int diff; ++ ++ if (!dp->buf_set) ++ return -EINVAL; ++ ++ if (copy_from_user(&w, arg, sizeof(w))) ++ return -EFAULT; ++ ++ if (w.clipcount) ++ return -EINVAL; ++ ++ /* ++ * Bound the overlay window by the size of the screen ++ */ ++ if (w.x < 0) ++ w.x = 0; ++ if (w.y < 0) ++ w.y = 0; ++ ++ if (w.x > dp->buf.width) ++ w.x = dp->buf.width; ++ if (w.y > dp->buf.height) ++ w.y = dp->buf.height; ++ ++ if (w.width < dp->capt.width) ++ w.width = dp->capt.width; ++ if (w.height < dp->capt.height) ++ w.height = dp->capt.height; ++ ++ if (w.x + w.width > dp->buf.width) ++ w.width = dp->buf.width - w.x; ++ if (w.y + w.height > dp->buf.height) ++ w.height = dp->buf.height - w.y; ++ ++ /* ++ * We've tried to make the values fit, but ++ * they just won't. ++ */ ++ if (w.width < dp->capt.width || w.height < dp->capt.height) ++ return -EINVAL; ++ ++ diff = dp->ovl->dst.x != w.x || ++ dp->ovl->dst.y != w.y || ++ dp->ovl->dst.width != w.width || ++ dp->ovl->dst.height != w.height || ++ dp->ovl->dst.chromakey != w.chromakey || ++ dp->ovl->dst.flags != w.flags; ++ ++ if (!dp->win_set || diff) { ++ dp->ovl->dst.x = w.x; ++ dp->ovl->dst.y = w.y; ++ dp->ovl->dst.width = w.width; ++ dp->ovl->dst.height = w.height; ++ dp->ovl->dst.chromakey = w.chromakey; ++ dp->ovl->dst.flags = w.flags; ++ ++ if (dp->ovl_active) ++ dp->ovl->ctl(dp, dp->ovl, 0); ++ ++ dp->ovl->set_win(dp, dp->ovl); ++ ++ if (dp->ovl_active) ++ dp->ovl->ctl(dp, dp->ovl, 1); ++ ++ diff = w.flags & VIDEO_WINDOW_INTERLACE ? 1 : 0; ++ if (!dp->win_set || dp->interlace != diff) { ++ dp->interlace = diff; ++ cyberpro_set_interlace(dp); ++ } ++ } ++ ++ dp->win_set = 1; ++ ++ return 0; ++ } ++ ++ case VIDIOCSFBUF: /* set frame buffer info */ ++ { ++ struct video_buffer b; ++ int ret; ++ ++ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) ++ return -EPERM; ++ ++ if (dp->cap_active) ++ return -EINVAL; ++ ++ if (copy_from_user(&b, arg, sizeof(b))) ++ return -EFAULT; ++ ++ ret = cyberpro_set_buffer(dp, &b); ++ if (ret == 0) { ++ dp->buf_set = 1; ++ dp->win_set = 0; ++ } ++ ++ return ret; ++ } ++ ++ case VIDIOCCAPTURE: ++ { ++ int on; ++ ++ if (get_user(on, (int *)arg)) ++ return -EFAULT; ++ ++ if (( on && dp->ovl_active) || ++ (!on && !dp->ovl_active)) ++ return 0; ++ ++ if (on && (!dp->buf_set || !dp->win_set)) ++ return -EINVAL; ++ ++ cyberpro_capture(dp, on); ++ dp->cap_active = on; ++ dp->ovl->ctl(dp, dp->ovl, on); ++ dp->ovl_active = on; ++ ++ return 0; ++ } ++ ++#ifdef USE_MMAP ++ case VIDIOCSYNC: ++ { ++ DECLARE_WAITQUEUE(wait, current); ++ int buf; ++ ++ /* ++ * The buffer must have been mmaped ++ * for this call to work. ++ */ ++ if (!dp->mmaped) ++ return -EINVAL; ++ ++ if (get_user(buf, (int *)arg)) ++ return -EFAULT; ++ ++ if (buf < 0 || buf >= NR_FRAMES) ++ return -EINVAL; ++ ++ switch (dp->frame[buf].status) { ++ case FRAME_FREE: ++ return -EINVAL; ++ ++ case FRAME_WAITING: ++ case FRAME_GRABBING: ++ add_wait_queue(&dp->frame_wait, &wait); ++ while (1) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (signal_pending(current)) ++ break; ++ if (dp->frame[buf].status == FRAME_DONE) ++ break; ++ schedule(); ++ } ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&dp->frame_wait, &wait); ++ if (signal_pending(current)) ++ return -EINTR; ++ /*FALLTHROUGH*/ ++ case FRAME_DONE: ++ dp->frame[buf].status = FRAME_FREE; ++ break; ++ } ++ return 0; ++ } ++ ++ case VIDIOCMCAPTURE: ++ { ++ struct video_mmap vmap; ++ ++ /* ++ * The buffer must have been mmaped ++ * for this call to work. ++ */ ++ if (!dp->mmaped) ++ return -EINVAL; ++ ++ if (copy_from_user(&vmap, arg, sizeof(vmap))) ++ return -EFAULT; ++ ++ /* ++ * We can only capture in our source format/size. ++ */ ++ if (vmap.frame >= NR_FRAMES || ++ vmap.format != dp->stream_fmt || ++ vmap.width != dp->capt.width || ++ vmap.height != dp->capt.height) ++ return -EINVAL; ++ ++ if (dp->frame[vmap.frame].status == FRAME_WAITING || ++ dp->frame[vmap.frame].status == FRAME_GRABBING) ++ return -EBUSY; ++ ++ dp->frame[vmap.frame].status = FRAME_WAITING; ++ return 0; ++ } ++ ++ case VIDIOCGMBUF: ++ { ++ struct video_mbuf vmb; ++ unsigned int i; ++ ++ vmb.frames = NR_FRAMES; ++ vmb.size = dp->frame_size * NR_FRAMES; ++ ++ for (i = 0; i < NR_FRAMES; i++) ++ vmb.offsets[i] = dp->frame[i].offset; ++ ++ return copy_to_user(arg, &vmb, sizeof(vmb)) ? -EFAULT : 0; ++ } ++#endif ++ ++ case VIDIOCSCAPTURE: ++ { ++ struct video_capture capt; ++ ++#ifndef ALLOW_SCAPTURE_WHILE_CAP ++ if (dp->cap_active) ++ return -EINVAL; ++#endif ++ ++ if (copy_from_user(&capt, arg, sizeof(capt))) ++ return -EFAULT; ++ ++ if (capt.x < 0 || capt.width < 0 || ++ capt.y < 0 || capt.height < 0 || ++ capt.x + capt.width > dp->cap.maxwidth || ++ capt.y + capt.height > dp->cap.maxheight) ++ return -EINVAL; ++ ++ /* ++ * The capture width must be a multiple of 4 ++ */ ++ if (dp->capt.width & 3) ++ return -EINVAL; ++ ++ dp->capt.x = capt.x; ++ dp->capt.y = capt.y; ++ dp->capt.width = capt.width; ++ dp->capt.height = capt.height; ++#ifdef ALLOW_SCAPTURE_WHILE_CAP ++ if (dp->ovl_active) ++ dp->ovl->ctl(dp, dp->ovl, 0); ++ if (dp->cap_active) ++ cyberpro_capture(dp, 0); ++#endif ++ cyberpro_capture_set_win(dp); ++ ++ /* ++ * Update the overlay window information ++ */ ++ dp->ovl->src.width = capt.width; ++ dp->ovl->src.height = capt.height; ++ ++ dp->ovl->set_src(dp, dp->ovl); ++ if (dp->win_set) ++ dp->ovl->set_win(dp, dp->ovl); ++ ++#ifdef ALLOW_SCAPTURE_WHILE_CAP ++ if (dp->cap_active) ++ cyberpro_capture(dp, 1); ++ if (dp->ovl_active) ++ dp->ovl->ctl(dp, dp->ovl, 1); ++#endif ++ return 0; ++ } ++ ++ case VIDIOCGTUNER: /* no tuner */ ++ case VIDIOCSTUNER: ++ return -EINVAL; ++ } ++ ++ return -EINVAL; ++} ++ ++#ifdef USE_MMAP ++static int ++cyberpro_grabber_mmap(struct video_device *dev, const char *addr, unsigned long size) ++{ ++ struct cyberpro_vidinfo *dp = dev->priv; ++ unsigned long vaddr = (unsigned long)addr; ++ pgprot_t prot; ++ int frame_idx, ret = -EINVAL; ++ ++#if defined(__arm__) ++ prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_USER | L_PTE_WRITE | L_PTE_DIRTY); ++#elif defined(__i386__) ++ prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED); ++ if (boot_cpu_data.x86 > 3) ++ pgprot_val(prot) |= _PAGE_PCD; ++#else ++#error "Unsupported architecture" ++#endif ++ ++ /* ++ * The mmap() request must have the correct size. ++ */ ++ if (size != NR_FRAMES * dp->frame_size) ++ goto out; ++ ++ /* ++ * If it's already mapped, don't re-do ++ */ ++ if (dp->mmaped) ++ goto out; ++ dp->mmaped = 1; ++ ++ /* ++ * Map in each frame ++ */ ++ for (frame_idx = 0; frame_idx < NR_FRAMES; frame_idx++) { ++ struct framebuf *frame; ++ int pgidx; ++ ++ frame = dp->frame + frame_idx; ++ ++ ret = cyberpro_alloc_frame_buffer(dp, frame); ++ ++ /* ++ * If an error occurs, we can be lazy and leave what we've ++ * been able to do. Our release function will free any ++ * allocated buffers, and do_mmap_pgoff() will zap any ++ * inserted mappings. ++ */ ++ if (ret) ++ goto out2; ++ ++ /* ++ * Map in each page on a page by page basis. This is just ++ * a little on the inefficient side, but it's only run once. ++ */ ++ for (pgidx = 0; pgidx < NR_PAGES; pgidx++) { ++ unsigned long virt; ++ ++ virt = page_address(frame->pages[pgidx]); ++ ++ ret = remap_page_range(vaddr, virt_to_phys((void *)virt), ++ PAGE_SIZE, prot); ++ ++ if (ret) ++ goto out2; ++ ++ vaddr += PAGE_SIZE; ++ } ++ } ++ ++ out2: ++ if (ret) ++ dp->mmaped = 0; ++ out: ++ return ret; ++} ++#endif ++ ++static int __init cyberpro_grabber_init_done(struct video_device *dev) ++{ ++ struct cyberpro_vidinfo *dp; ++ struct cyberpro_info *info = dev->priv; ++ int ret; ++ ++ dp = kmalloc(sizeof(*dp), GFP_KERNEL); ++ if (!dp) ++ return -ENOMEM; ++ ++ memset(dp, 0, sizeof(*dp)); ++ ++ dev->priv = dp; ++ dp->info = *info; ++ dp->dev = dev; ++ dp->bus = &cyberpro_i2c_bus; ++ dp->regs = info->regs; ++ dp->irq = info->dev->irq; ++ ++ strcpy(dp->cap.name, dev->name); ++ dp->cap.type = dev->type; ++ dp->cap.channels = 1; ++ dp->cap.audios = 0; ++ dp->cap.minwidth = 32; ++ dp->cap.maxwidth = 716; ++ dp->cap.minheight = 32; ++ dp->cap.maxheight = 576; ++ ++ dp->pic.brightness = 32768; ++ dp->pic.hue = 32768; ++ dp->pic.colour = 32768; ++ dp->pic.contrast = 32768; ++ dp->pic.whiteness = 0; ++ dp->pic.depth = 8; ++ dp->pic.palette = VIDEO_PALETTE_YUV422; ++ ++ /* dp->buf is setup by the user */ ++ /* dp->cap_mem_offset setup by dp->buf */ ++ ++ dp->norm = VIDEO_MODE_AUTO; ++ ++ /* ++ * The extended overlay window ++ */ ++ dp->ext.init = cyberpro_ext_init; ++ dp->ext.set_src = cyberpro_ext_set_src; ++ dp->ext.set_win = cyberpro_ext_set_win; ++ dp->ext.ctl = cyberpro_ext_ctl; ++ ++ /* ++ * The V2 overlay window ++ */ ++ dp->v2.init = cyberpro_v2_init; ++ dp->v2.set_src = cyberpro_v2_set_src; ++ dp->v2.set_win = cyberpro_v2_set_win; ++ dp->v2.ctl = cyberpro_v2_ctl; ++ ++ /* ++ * The X2 overlay window ++ */ ++ dp->x2.init = cyberpro_x2_init; ++ dp->x2.set_src = cyberpro_x2_set_src; ++ dp->x2.set_win = cyberpro_x2_set_win; ++ dp->x2.ctl = cyberpro_x2_ctl; ++ ++ /* ++ * Set the overlay window which we shall be using ++ */ ++ dp->ovl = &dp->ext; ++ ++ dp->cap_mode1 = EXT_CAP_MODE1_ALTFIFO; ++ ++ /* ++ * Initialise hardware specific values. ++ * - CCIR656 8bit mode (YUV422 data) ++ * - Ignore Hgood signal ++ * - Invert Odd/Even field signal ++ */ ++ dp->cap_mode1 |= EXT_CAP_MODE1_CCIR656 | EXT_CAP_MODE1_8BIT; ++ dp->cap_mode2 = EXT_CAP_MODE2_FIXSONY | EXT_CAP_MODE2_DATEND | ++ EXT_CAP_MODE2_CCIRINVOE; ++ dp->stream_fmt = VIDEO_PALETTE_YUV422; ++ ++ ++ init_waitqueue_head(&dp->vbl_wait); ++ cyberpro_frames_init(dp); ++ ++ /* ++ * wake up the decoder ++ */ ++ decoder_sleep(0); ++ ++ dp->bus->data = dp; ++ strncpy(dp->bus->name, dev->name, sizeof(dp->bus->name)); ++ ++ pci_set_master(dp->info.dev); ++ ++ ret = i2c_register_bus(dp->bus); ++ ++ /* ++ * If we successfully registered the bus, but didn't initialise ++ * the decoder (because its driver is not present), request ++ * that it is loaded. ++ */ ++ if (ret == 0 && !dp->decoder) ++ request_module("saa7111"); ++ ++ /* ++ * If that didn't work, then we're out of luck. ++ */ ++ if (ret == 0 && !dp->decoder) { ++ i2c_unregister_bus(dp->bus); ++ ret = -ENXIO; ++ } ++ ++ if (ret) { ++ kfree(dp); ++ ++ /* ++ * put the decoder back to sleep ++ */ ++ decoder_sleep(1); ++ } ++ ++ return ret; ++} ++ ++static struct video_device cyberpro_grabber = { ++ name: "", ++ type: VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | ++ VID_TYPE_CHROMAKEY | VID_TYPE_SCALES | ++ VID_TYPE_SUBCAPTURE, ++ hardware: 0, ++ open: cyberpro_grabber_open, ++ close: cyberpro_grabber_close, ++ read: cyberpro_grabber_read, ++ write: cyberpro_grabber_write, ++ ioctl: cyberpro_grabber_ioctl, ++#ifdef USE_MMAP ++ mmap: cyberpro_grabber_mmap, ++#endif ++ initialize: cyberpro_grabber_init_done, ++}; ++ ++int init_cyber2000fb_viddev(void) ++{ ++ struct cyberpro_info info; ++ ++ if (!cyber2000fb_attach(&info, 0)) ++ return -ENXIO; ++ ++ strncpy(cyberpro_grabber.name, info.dev_name, sizeof(cyberpro_grabber.name)); ++ ++ cyberpro_grabber.priv = &info; ++ ++ return video_register_device(&cyberpro_grabber, VFL_TYPE_GRABBER, -1); ++} ++ ++/* ++ * This can be cleaned up when the SAA7111 code is fixed. ++ */ ++#ifdef MODULE ++static int __init cyberpro_init(void) ++{ ++ disable_irq(35); ++ return init_cyber2000fb_viddev(); ++} ++ ++static void __exit cyberpro_exit(void) ++{ ++ video_unregister_device(&cyberpro_grabber); ++ kfree(cyberpro_grabber.priv); ++ i2c_unregister_bus(&cyberpro_i2c_bus); ++ ++ /* ++ * put the decoder back to sleep ++ */ ++ decoder_sleep(1); ++ ++ cyber2000fb_detach(0); ++} ++ ++module_init(cyberpro_init); ++module_exit(cyberpro_exit); ++#endif +diff -urN linux-2.4.26/drivers/media/video/i2c-old.c linux-2.4.26-vrs1/drivers/media/video/i2c-old.c +--- linux-2.4.26/drivers/media/video/i2c-old.c 2004-02-27 20:03:26.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/media/video/i2c-old.c 2004-02-27 23:41:21.000000000 +0000 +@@ -36,11 +36,20 @@ + static struct i2c_driver *drivers[I2C_DRIVER_MAX]; + static int bus_count = 0, driver_count = 0; + ++extern int saa7111_init(void); ++extern int saa7185_init(void); ++extern int bt819_init(void); ++extern int bt856_init(void); ++ + int i2c_init(void) + { + printk(KERN_INFO "i2c: initialized%s\n", + scan ? " (i2c bus scan enabled)" : ""); + ++#if defined(CONFIG_VIDEO_CYBERPRO) ++ saa7111_init(); ++#endif ++ + return 0; + } + +@@ -52,10 +61,10 @@ + int i,j,ack=1; + unsigned char addr; + LOCK_FLAGS; +- ++ + /* probe for device */ + LOCK_I2C_BUS(bus); +- for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2) ++ for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2) + { + i2c_start(bus); + ack = i2c_sendbyte(bus,addr,0); +@@ -87,8 +96,8 @@ + device->addr = addr; + + /* Attach */ +- +- if (driver->attach(device)!=0) ++ ++ if (driver->attach(device)!=0) + { + kfree(device); + return; +@@ -114,7 +123,7 @@ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (device == device->driver->devices[i]) + break; +- if (I2C_DEVICE_MAX == i) ++ if (I2C_DEVICE_MAX == i) + { + printk(KERN_WARNING "i2c: detach_device #1: device not found: %s\n", + device->name); +@@ -126,7 +135,7 @@ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (device == device->bus->devices[i]) + break; +- if (I2C_DEVICE_MAX == i) ++ if (I2C_DEVICE_MAX == i) + { + printk(KERN_WARNING "i2c: detach_device #2: device not found: %s\n", + device->name); +@@ -158,19 +167,19 @@ + busses[i] = bus; + bus_count++; + REGPRINT(printk("i2c: bus registered: %s\n",bus->name)); +- ++ + MOD_INC_USE_COUNT; + +- if (scan) ++ if (scan) + { + /* scan whole i2c bus */ + LOCK_I2C_BUS(bus); +- for (i = 0; i < 256; i+=2) ++ for (i = 0; i < 256; i+=2) + { + i2c_start(bus); + ack = i2c_sendbyte(bus,i,0); + i2c_stop(bus); +- if (!ack) ++ if (!ack) + { + printk(KERN_INFO "i2c: scanning bus %s: found device at addr=0x%02x\n", + bus->name,i); +@@ -198,20 +207,20 @@ + for (i = 0; i < I2C_BUS_MAX; i++) + if (bus == busses[i]) + break; +- if (I2C_BUS_MAX == i) ++ if (I2C_BUS_MAX == i) + { + printk(KERN_WARNING "i2c: unregister_bus #1: bus not found: %s\n", + bus->name); + return -ENODEV; + } +- ++ + MOD_DEC_USE_COUNT; +- ++ + busses[i] = NULL; + bus_count--; + REGPRINT(printk("i2c: bus unregistered: %s\n",bus->name)); + +- return 0; ++ return 0; + } + + /* ----------------------------------------------------------------------- */ +@@ -231,9 +240,9 @@ + + drivers[i] = driver; + driver_count++; +- ++ + MOD_INC_USE_COUNT; +- ++ + REGPRINT(printk("i2c: driver registered: %s\n",driver->name)); + + /* Probe available busses */ +@@ -256,7 +265,7 @@ + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (driver == drivers[i]) + break; +- if (I2C_DRIVER_MAX == i) ++ if (I2C_DRIVER_MAX == i) + { + printk(KERN_WARNING "i2c: unregister_driver: driver not found: %s\n", + driver->name); +@@ -264,7 +273,7 @@ + } + + MOD_DEC_USE_COUNT; +- ++ + drivers[i] = NULL; + driver_count--; + REGPRINT(printk("i2c: driver unregistered: %s\n",driver->name)); +@@ -328,7 +337,7 @@ + int i2c_ack(struct i2c_bus *bus) + { + int ack; +- ++ + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + ack = I2C_GET(bus); +@@ -339,7 +348,7 @@ + int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack) + { + int i, ack; +- ++ + I2C_SET(bus,0,0); + for (i=7; i>=0; i--) + (data&(1<=0; i--) ++ for (i=7; i>=0; i--) + { + I2C_SET(bus,1,1); + if (I2C_GET(bus)) +@@ -373,7 +382,7 @@ + int i2c_read(struct i2c_bus *bus, unsigned char addr) + { + int ret; +- ++ + if (bus->i2c_read) + return bus->i2c_read(bus, addr); + +diff -urN linux-2.4.26/drivers/media/video/saa7111.c linux-2.4.26-vrs1/drivers/media/video/saa7111.c +--- linux-2.4.26/drivers/media/video/saa7111.c 2001-09-30 20:26:06.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/media/video/saa7111.c 2004-01-14 21:32:25.000000000 +0000 +@@ -20,9 +20,9 @@ + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +- +-#include ++#include + #include ++#include + #include + #include + #include +@@ -149,7 +149,11 @@ + 0x0d, 0x00, /* 0d - HUE=0 */ + 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */ + 0x0f, 0x00, /* 0f - reserved */ ++#ifndef CONFIG_ARCH_NETWINDER + 0x10, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ ++#else ++ 0x10, 0xc8, /* 10 - OFTS=YUV-CCIR656, HDEL=0, VLRN=1, YDEL=0 */ ++#endif + 0x11, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */ + 0x12, 0x00, /* 12 - output control 2 */ + 0x13, 0x00, /* 13 - output control 3 */ +diff -urN linux-2.4.26/drivers/message/i2o/i2o_core.c linux-2.4.26-vrs1/drivers/message/i2o/i2o_core.c +--- linux-2.4.26/drivers/message/i2o/i2o_core.c 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/message/i2o/i2o_core.c 2004-01-14 21:32:25.000000000 +0000 +@@ -1665,14 +1665,14 @@ + } + memset(status, 0, 4); + +- msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0; +- msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID; +- msg[2]=core_context; +- msg[3]=0; +- msg[4]=0; +- msg[5]=0; +- msg[6]=virt_to_bus(status); +- msg[7]=0; /* 64bit host FIXME */ ++ writel(EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0, msg + 0); ++ writel(I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID, msg + 1); ++ writel(core_context, msg + 2); ++ writel(0, msg + 3); ++ writel(0, msg + 4); ++ writel(0, msg + 5); ++ writel(virt_to_bus(status), msg + 6); ++ writel(0, msg + 7); /* 64bit host FIXME */ + + i2o_post_message(c,m); + +@@ -1781,15 +1781,15 @@ + return -ETIMEDOUT; + msg=(u32 *)(c->mem_offset+m); + +- msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_0; +- msg[1]=I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID; +- msg[2]=core_context; +- msg[3]=0; +- msg[4]=0; +- msg[5]=0; +- msg[6]=virt_to_bus(c->status_block); +- msg[7]=0; /* 64bit host FIXME */ +- msg[8]=sizeof(i2o_status_block); /* always 88 bytes */ ++ writel(NINE_WORD_MSG_SIZE|SGL_OFFSET_0, msg + 0); ++ writel(I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID, msg + 1); ++ writel(core_context, msg + 2); ++ writel(0, msg + 3); ++ writel(0, msg + 4); ++ writel(0, msg + 5); ++ writel(virt_to_bus(c->status_block), msg + 6); ++ writel(0, msg + 7); /* 64bit host FIXME */ ++ writel(sizeof(i2o_status_block), msg + 8); /* always 88 bytes */ + + i2o_post_message(c,m); + +@@ -2193,15 +2193,15 @@ + } + memset(status, 0, 4); + +- msg[0]= EIGHT_WORD_MSG_SIZE| TRL_OFFSET_6; +- msg[1]= I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID; +- msg[2]= core_context; +- msg[3]= 0x0106; /* Transaction context */ +- msg[4]= 4096; /* Host page frame size */ ++ writel(EIGHT_WORD_MSG_SIZE| TRL_OFFSET_6, msg + 0); ++ writel(I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID, msg + 1); ++ writel(core_context, msg + 2); ++ writel(0x0106, msg + 3); /* Transaction context */ ++ writel(PAGE_SIZE, msg + 4); /* Host page frame size */ + /* Frame size is in words. 256 bytes a frame for now */ +- msg[5]= MSG_FRAME_SIZE<<16|0x80; /* Outbound msg frame size in words and Initcode */ +- msg[6]= 0xD0000004; /* Simple SG LE, EOB */ +- msg[7]= virt_to_bus(status); ++ writel(MSG_FRAME_SIZE<<16|0x80, msg + 5);/* Outbound msg frame size in words and Initcode */ ++ writel(0xD0000004, msg + 6); /* Simple SG LE, EOB */ ++ writel(virt_to_bus(status), msg + 7); + + i2o_post_message(c,m); + +diff -urN linux-2.4.26/drivers/message/i2o/i2o_pci.c linux-2.4.26-vrs1/drivers/message/i2o/i2o_pci.c +--- linux-2.4.26/drivers/message/i2o/i2o_pci.c 2002-11-28 23:53:13.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/message/i2o/i2o_pci.c 2004-01-14 21:32:25.000000000 +0000 +@@ -390,4 +390,4 @@ + MODULE_PARM_DESC(dpt, "Set this if you want to drive DPT cards normally handled by dpt_i2o"); + module_init(i2o_pci_core_attach); + module_exit(i2o_pci_core_detach); +- +\ No newline at end of file ++ +diff -urN linux-2.4.26/drivers/misc/Config.in linux-2.4.26-vrs1/drivers/misc/Config.in +--- linux-2.4.26/drivers/misc/Config.in 1999-12-25 23:04:56.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/misc/Config.in 2004-01-14 21:32:25.000000000 +0000 +@@ -1,7 +1,17 @@ + # +-# Misc strange devices ++# MCP drivers + # + mainmenu_option next_comment +-comment 'Misc devices' ++comment 'Multimedia Capabilities Port drivers' ++ ++bool 'Multimedia drivers' CONFIG_MCP ++ ++# Interface drivers ++dep_bool 'Support SA1100 MCP interface' CONFIG_MCP_SA1100 $CONFIG_MCP $CONFIG_ARCH_SA1100 ++ ++# Chip drivers ++dep_tristate 'Support for UCB1200 / UCB1300' CONFIG_MCP_UCB1200 $CONFIG_MCP ++dep_tristate ' Audio / Telephony interface support' CONFIG_MCP_UCB1200_AUDIO $CONFIG_MCP_UCB1200 $CONFIG_SOUND ++dep_tristate ' Touchscreen interface support' CONFIG_MCP_UCB1200_TS $CONFIG_MCP_UCB1200 + + endmenu +diff -urN linux-2.4.26/drivers/misc/Makefile linux-2.4.26-vrs1/drivers/misc/Makefile +--- linux-2.4.26/drivers/misc/Makefile 2000-12-29 22:07:22.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/misc/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -11,6 +11,14 @@ + + O_TARGET := misc.o + ++export-objs := mcp-core.o mcp-sa1100.o ucb1x00-core.o ++ ++obj-$(CONFIG_MCP) += mcp-core.o ++obj-$(CONFIG_MCP_SA1100) += mcp-sa1100.o ++obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o ++obj-$(CONFIG_MCP_UCB1200_AUDIO) += ucb1x00-audio.o ++obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o ++ + include $(TOPDIR)/Rules.make + + fastdep: +diff -urN linux-2.4.26/drivers/misc/mcp-core.c linux-2.4.26-vrs1/drivers/misc/mcp-core.c +--- linux-2.4.26/drivers/misc/mcp-core.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/misc/mcp-core.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,155 @@ ++/* ++ * linux/drivers/misc/mcp-core.c ++ * ++ * Copyright (C) 2001 Russell King ++ * ++ * 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. ++ * ++ * Generic MCP (Multimedia Communications Port) layer. All MCP locking ++ * is solely held within this file. ++ */ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "mcp.h" ++ ++/** ++ * mcp_set_telecom_divisor - set the telecom divisor ++ * @mcp: MCP interface structure ++ * @div: SIB clock divisor ++ * ++ * Set the telecom divisor on the MCP interface. The resulting ++ * sample rate is SIBCLOCK/div. ++ */ ++void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div) ++{ ++ spin_lock_irq(&mcp->lock); ++ mcp->set_telecom_divisor(mcp, div); ++ spin_unlock_irq(&mcp->lock); ++} ++ ++/** ++ * mcp_set_audio_divisor - set the audio divisor ++ * @mcp: MCP interface structure ++ * @div: SIB clock divisor ++ * ++ * Set the audio divisor on the MCP interface. ++ */ ++void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div) ++{ ++ spin_lock_irq(&mcp->lock); ++ mcp->set_audio_divisor(mcp, div); ++ spin_unlock_irq(&mcp->lock); ++} ++ ++/** ++ * mcp_reg_write - write a device register ++ * @mcp: MCP interface structure ++ * @reg: 4-bit register index ++ * @val: 16-bit data value ++ * ++ * Write a device register. The MCP interface must be enabled ++ * to prevent this function hanging. ++ */ ++void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&mcp->lock, flags); ++ mcp->reg_write(mcp, reg, val); ++ spin_unlock_irqrestore(&mcp->lock, flags); ++} ++ ++/** ++ * mcp_reg_read - read a device register ++ * @mcp: MCP interface structure ++ * @reg: 4-bit register index ++ * ++ * Read a device register and return its value. The MCP interface ++ * must be enabled to prevent this function hanging. ++ */ ++unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg) ++{ ++ unsigned long flags; ++ unsigned int val; ++ ++ spin_lock_irqsave(&mcp->lock, flags); ++ val = mcp->reg_read(mcp, reg); ++ spin_unlock_irqrestore(&mcp->lock, flags); ++ ++ return val; ++} ++ ++/** ++ * mcp_enable - enable the MCP interface ++ * @mcp: MCP interface to enable ++ * ++ * Enable the MCP interface. Each call to mcp_enable will need ++ * a corresponding call to mcp_disable to disable the interface. ++ */ ++void mcp_enable(struct mcp *mcp) ++{ ++ spin_lock_irq(&mcp->lock); ++ if (mcp->use_count++ == 0) ++ mcp->enable(mcp); ++ spin_unlock_irq(&mcp->lock); ++} ++ ++/** ++ * mcp_disable - disable the MCP interface ++ * @mcp: MCP interface to disable ++ * ++ * Disable the MCP interface. The MCP interface will only be ++ * disabled once the number of calls to mcp_enable matches the ++ * number of calls to mcp_disable. ++ */ ++void mcp_disable(struct mcp *mcp) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&mcp->lock, flags); ++ if (--mcp->use_count == 0) ++ mcp->disable(mcp); ++ spin_unlock_irqrestore(&mcp->lock, flags); ++} ++ ++ ++/* ++ * This needs re-working ++ */ ++static struct mcp *mcp_if; ++ ++struct mcp *mcp_get(void) ++{ ++ return mcp_if; ++} ++ ++int mcp_register(struct mcp *mcp) ++{ ++ if (mcp_if) ++ return -EBUSY; ++ if (mcp->owner) ++ __MOD_INC_USE_COUNT(mcp->owner); ++ mcp_if = mcp; ++ return 0; ++} ++ ++EXPORT_SYMBOL(mcp_set_telecom_divisor); ++EXPORT_SYMBOL(mcp_set_audio_divisor); ++EXPORT_SYMBOL(mcp_reg_write); ++EXPORT_SYMBOL(mcp_reg_read); ++EXPORT_SYMBOL(mcp_enable); ++EXPORT_SYMBOL(mcp_disable); ++EXPORT_SYMBOL(mcp_get); ++EXPORT_SYMBOL(mcp_register); ++ ++MODULE_AUTHOR("Russell King "); ++MODULE_DESCRIPTION("Core multimedia communications port driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/misc/mcp-sa1100.c linux-2.4.26-vrs1/drivers/misc/mcp-sa1100.c +--- linux-2.4.26/drivers/misc/mcp-sa1100.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/misc/mcp-sa1100.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,180 @@ ++/* ++ * linux/drivers/misc/mcp-sa1100.c ++ * ++ * Copyright (C) 2001 Russell King ++ * ++ * 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. ++ * ++ * SA1100 MCP (Multimedia Communications Port) driver. ++ * ++ * MCP read/write timeouts from Jordi Colomer, rehacked by rmk. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "mcp.h" ++ ++static void ++mcp_sa1100_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) ++{ ++ unsigned int mccr0; ++ ++ divisor /= 32; ++ ++ mccr0 = Ser4MCCR0 & ~0x00007f00; ++ mccr0 |= divisor << 8; ++ Ser4MCCR0 = mccr0; ++} ++ ++static void ++mcp_sa1100_set_audio_divisor(struct mcp *mcp, unsigned int divisor) ++{ ++ unsigned int mccr0; ++ ++ divisor /= 32; ++ ++ mccr0 = Ser4MCCR0 & ~0x0000007f; ++ mccr0 |= divisor; ++ Ser4MCCR0 = mccr0; ++} ++ ++/* ++ * Write data to the device. The bit should be set after 3 subframe ++ * times (each frame is 64 clocks). We wait a maximum of 6 subframes. ++ * We really should try doing something more productive while we ++ * wait. ++ */ ++static void ++mcp_sa1100_write(struct mcp *mcp, unsigned int reg, unsigned int val) ++{ ++ int ret = -ETIME; ++ int i; ++ ++ Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); ++ ++ for (i = 0; i < 2; i++) { ++ udelay(mcp->rw_timeout); ++ if (Ser4MCSR & MCSR_CWC) { ++ ret = 0; ++ break; ++ } ++ } ++ ++ if (ret < 0) ++ printk(KERN_WARNING "mcp: write timed out\n"); ++} ++ ++/* ++ * Read data from the device. The bit should be set after 3 subframe ++ * times (each frame is 64 clocks). We wait a maximum of 6 subframes. ++ * We really should try doing something more productive while we ++ * wait. ++ */ ++static unsigned int ++mcp_sa1100_read(struct mcp *mcp, unsigned int reg) ++{ ++ int ret = -ETIME; ++ int i; ++ ++ Ser4MCDR2 = reg << 17 | MCDR2_Rd; ++ ++ for (i = 0; i < 2; i++) { ++ udelay(mcp->rw_timeout); ++ if (Ser4MCSR & MCSR_CRC) { ++ ret = Ser4MCDR2 & 0xffff; ++ break; ++ } ++ } ++ ++ if (ret < 0) ++ printk(KERN_WARNING "mcp: read timed out\n"); ++ ++ return ret; ++} ++ ++static void mcp_sa1100_enable(struct mcp *mcp) ++{ ++ Ser4MCSR = -1; ++ Ser4MCCR0 |= MCCR0_MCE; ++} ++ ++static void mcp_sa1100_disable(struct mcp *mcp) ++{ ++ Ser4MCCR0 &= ~MCCR0_MCE; ++} ++ ++struct mcp mcp_sa1100 = { ++ owner: THIS_MODULE, ++ lock: SPIN_LOCK_UNLOCKED, ++ sclk_rate: 11981000, ++ dma_audio_rd: DMA_Ser4MCP0Rd, ++ dma_audio_wr: DMA_Ser4MCP0Wr, ++ dma_telco_rd: DMA_Ser4MCP1Rd, ++ dma_telco_wr: DMA_Ser4MCP1Wr, ++ set_telecom_divisor: mcp_sa1100_set_telecom_divisor, ++ set_audio_divisor: mcp_sa1100_set_audio_divisor, ++ reg_write: mcp_sa1100_write, ++ reg_read: mcp_sa1100_read, ++ enable: mcp_sa1100_enable, ++ disable: mcp_sa1100_disable, ++}; ++ ++/* ++ * This needs re-working ++ */ ++static int mcp_sa1100_init(void) ++{ ++ struct mcp *mcp = &mcp_sa1100; ++ int ret = -ENODEV; ++ ++ if (machine_is_accelent_sa() || ++ machine_is_adsbitsy() || machine_is_assabet() || ++ machine_is_cerf() || machine_is_flexanet() || ++ machine_is_freebird() || machine_is_graphicsclient() || ++ machine_is_graphicsmaster() || machine_is_lart() || ++ machine_is_omnimeter() || machine_is_pfs168() || ++ machine_is_shannon() || machine_is_simpad() || ++ machine_is_simputer() || machine_is_yopy()) { ++ /* ++ * Setup the PPC unit correctly. ++ */ ++ PPDR &= ~PPC_RXD4; ++ PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; ++ PSDR |= PPC_RXD4; ++ PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); ++ PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); ++ ++ Ser4MCSR = -1; ++ Ser4MCCR1 = 0; ++ Ser4MCCR0 = 0x00007f7f | MCCR0_ADM; ++ ++ /* ++ * Calculate the read/write timeout (us) from the bit clock ++ * rate. This is the period for 3 64-bit frames. Always ++ * round this time up. ++ */ ++ mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / ++ mcp->sclk_rate; ++ ++ ret = mcp_register(mcp); ++ } ++ ++ return ret; ++} ++ ++module_init(mcp_sa1100_init); ++EXPORT_SYMBOL(mcp_sa1100_init); ++ ++MODULE_AUTHOR("Russell King "); ++MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/misc/mcp.h linux-2.4.26-vrs1/drivers/misc/mcp.h +--- linux-2.4.26/drivers/misc/mcp.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/misc/mcp.h 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,44 @@ ++/* ++ * linux/drivers/misc/mcp.h ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * 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. ++ */ ++#ifndef MCP_H ++#define MCP_H ++ ++struct mcp { ++ struct module *owner; ++ spinlock_t lock; ++ int use_count; ++ unsigned int sclk_rate; ++ unsigned int rw_timeout; ++ dma_device_t dma_audio_rd; ++ dma_device_t dma_audio_wr; ++ dma_device_t dma_telco_rd; ++ dma_device_t dma_telco_wr; ++ void (*set_telecom_divisor)(struct mcp *, unsigned int); ++ void (*set_audio_divisor)(struct mcp *, unsigned int); ++ void (*reg_write)(struct mcp *, unsigned int, unsigned int); ++ unsigned int (*reg_read)(struct mcp *, unsigned int); ++ void (*enable)(struct mcp *); ++ void (*disable)(struct mcp *); ++}; ++ ++void mcp_set_telecom_divisor(struct mcp *, unsigned int); ++void mcp_set_audio_divisor(struct mcp *, unsigned int); ++void mcp_reg_write(struct mcp *, unsigned int, unsigned int); ++unsigned int mcp_reg_read(struct mcp *, unsigned int); ++void mcp_enable(struct mcp *); ++void mcp_disable(struct mcp *); ++ ++/* noddy implementation alert! */ ++struct mcp *mcp_get(void); ++int mcp_register(struct mcp *); ++ ++#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) ++ ++#endif +diff -urN linux-2.4.26/drivers/misc/ucb1x00-audio.c linux-2.4.26-vrs1/drivers/misc/ucb1x00-audio.c +--- linux-2.4.26/drivers/misc/ucb1x00-audio.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/misc/ucb1x00-audio.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,378 @@ ++/* ++ * linux/drivers/misc/ucb1x00-audio.c ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "ucb1x00.h" ++ ++#include "../drivers/sound/sa1100-audio.h" ++ ++#define MAGIC 0x41544154 ++ ++struct ucb1x00_audio { ++ struct file_operations fops; ++ struct file_operations mops; ++ struct ucb1x00 *ucb; ++ audio_stream_t output_stream; ++ audio_stream_t input_stream; ++ audio_state_t state; ++ unsigned int rate; ++ int dev_id; ++ int mix_id; ++ unsigned int daa_oh_bit; ++ unsigned int telecom; ++ unsigned int magic; ++ unsigned int ctrl_a; ++ unsigned int ctrl_b; ++ ++ /* mixer info */ ++ unsigned int mod_cnt; ++ unsigned short output_level; ++ unsigned short input_level; ++}; ++ ++#define REC_MASK (SOUND_MASK_VOLUME | SOUND_MASK_MIC) ++#define DEV_MASK REC_MASK ++ ++static int ++ucb1x00_mixer_ioctl(struct inode *ino, struct file *filp, uint cmd, ulong arg) ++{ ++ struct ucb1x00_audio *ucba; ++ unsigned int val, gain; ++ int ret = 0; ++ ++ ucba = list_entry(filp->f_op, struct ucb1x00_audio, mops); ++ ++ if (_IOC_TYPE(cmd) != 'M') ++ return -EINVAL; ++ ++ if (cmd == SOUND_MIXER_INFO) { ++ struct mixer_info mi; ++ ++ strncpy(mi.id, "UCB1x00", sizeof(mi.id)); ++ strncpy(mi.name, "Philips UCB1x00", sizeof(mi.name)); ++ mi.modify_counter = ucba->mod_cnt; ++ return copy_to_user((void *)arg, &mi, sizeof(mi)) ? -EFAULT : 0; ++ } ++ ++ if (_IOC_DIR(cmd) & _IOC_WRITE) { ++ unsigned int left, right; ++ ++ ret = get_user(val, (unsigned int *)arg); ++ if (ret) ++ goto out; ++ ++ left = val & 255; ++ right = val >> 8; ++ ++ if (left > 100) ++ left = 100; ++ if (right > 100) ++ right = 100; ++ ++ gain = (left + right) / 2; ++ ++ ret = -EINVAL; ++ if (!ucba->telecom) { ++ switch(_IOC_NR(cmd)) { ++ case SOUND_MIXER_VOLUME: ++ ucba->output_level = gain | gain << 8; ++ ucba->mod_cnt++; ++ ucba->ctrl_b = (ucba->ctrl_b & 0xff00) | ++ ((gain * 31) / 100); ++ ucb1x00_reg_write(ucba->ucb, UCB_AC_B, ++ ucba->ctrl_b); ++ ret = 0; ++ break; ++ ++ case SOUND_MIXER_MIC: ++ ucba->input_level = gain | gain << 8; ++ ucba->mod_cnt++; ++ ucba->ctrl_a = (ucba->ctrl_a & 0x7f) | ++ (((gain * 31) / 100) << 7); ++ ucb1x00_reg_write(ucba->ucb, UCB_AC_A, ++ ucba->ctrl_a); ++ ret = 0; ++ break; ++ } ++ } ++ } ++ ++ if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { ++ switch (_IOC_NR(cmd)) { ++ case SOUND_MIXER_VOLUME: ++ val = ucba->output_level; ++ break; ++ ++ case SOUND_MIXER_MIC: ++ val = ucba->input_level; ++ break; ++ ++ case SOUND_MIXER_RECSRC: ++ case SOUND_MIXER_RECMASK: ++ val = ucba->telecom ? 0 : REC_MASK; ++ break; ++ ++ case SOUND_MIXER_DEVMASK: ++ val = ucba->telecom ? 0 : DEV_MASK; ++ break; ++ ++ case SOUND_MIXER_CAPS: ++ case SOUND_MIXER_STEREODEVS: ++ val = 0; ++ break; ++ ++ default: ++ val = 0; ++ ret = -EINVAL; ++ break; ++ } ++ ++ if (ret == 0) ++ ret = put_user(val, (int *)arg); ++ } ++ out: ++ return ret; ++} ++ ++static int ucb1x00_audio_setrate(struct ucb1x00_audio *ucba, int rate) ++{ ++ unsigned int div_rate = ucb1x00_clkrate(ucba->ucb) / 32; ++ unsigned int div; ++ ++ div = (div_rate + (rate / 2)) / rate; ++ if (div < 6) ++ div = 6; ++ if (div > 127) ++ div = 127; ++ ++ ucba->ctrl_a = (ucba->ctrl_a & ~0x7f) | div; ++ ++ if (ucba->telecom) { ++ ucb1x00_reg_write(ucba->ucb, UCB_TC_B, 0); ++ ucb1x00_set_telecom_divisor(ucba->ucb, div * 32); ++ ucb1x00_reg_write(ucba->ucb, UCB_TC_A, ucba->ctrl_a); ++ ucb1x00_reg_write(ucba->ucb, UCB_TC_B, ucba->ctrl_b); ++ } else { ++ ucb1x00_reg_write(ucba->ucb, UCB_AC_B, 0); ++ ucb1x00_set_audio_divisor(ucba->ucb, div * 32); ++ ucb1x00_reg_write(ucba->ucb, UCB_AC_A, ucba->ctrl_a); ++ ucb1x00_reg_write(ucba->ucb, UCB_AC_B, ucba->ctrl_b); ++ } ++ ++ ucba->rate = div_rate / div; ++ ++ return ucba->rate; ++} ++ ++static int ucb1x00_audio_getrate(struct ucb1x00_audio *ucba) ++{ ++ return ucba->rate; ++} ++ ++static void ucb1x00_audio_startup(void *data) ++{ ++ struct ucb1x00_audio *ucba = data; ++ ++ ucb1x00_enable(ucba->ucb); ++ ucb1x00_audio_setrate(ucba, ucba->rate); ++ ++ ucb1x00_reg_write(ucba->ucb, UCB_MODE, UCB_MODE_DYN_VFLAG_ENA); ++ ++ /* ++ * Take off-hook ++ */ ++ if (ucba->daa_oh_bit) ++ ucb1x00_io_write(ucba->ucb, 0, ucba->daa_oh_bit); ++} ++ ++static void ucb1x00_audio_shutdown(void *data) ++{ ++ struct ucb1x00_audio *ucba = data; ++ ++ /* ++ * Place on-hook ++ */ ++ if (ucba->daa_oh_bit) ++ ucb1x00_io_write(ucba->ucb, ucba->daa_oh_bit, 0); ++ ++ ucb1x00_reg_write(ucba->ucb, ucba->telecom ? UCB_TC_B : UCB_AC_B, 0); ++ ucb1x00_disable(ucba->ucb); ++} ++ ++static int ++ucb1x00_audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) ++{ ++ struct ucb1x00_audio *ucba; ++ int val, ret = 0; ++ ++ ucba = list_entry(file->f_op, struct ucb1x00_audio, fops); ++ ++ /* ++ * Make sure we have our magic number ++ */ ++ if (ucba->magic != MAGIC) ++ return -ENODEV; ++ ++ switch (cmd) { ++ case SNDCTL_DSP_STEREO: ++ ret = get_user(val, (int *)arg); ++ if (ret) ++ return ret; ++ if (val != 0) ++ return -EINVAL; ++ val = 0; ++ break; ++ ++ case SNDCTL_DSP_CHANNELS: ++ case SOUND_PCM_READ_CHANNELS: ++ val = 1; ++ break; ++ ++ case SNDCTL_DSP_SPEED: ++ ret = get_user(val, (int *)arg); ++ if (ret) ++ return ret; ++ val = ucb1x00_audio_setrate(ucba, val); ++ break; ++ ++ case SOUND_PCM_READ_RATE: ++ val = ucb1x00_audio_getrate(ucba); ++ break; ++ ++ case SNDCTL_DSP_SETFMT: ++ case SNDCTL_DSP_GETFMTS: ++ val = AFMT_S16_LE; ++ break; ++ ++ default: ++ return ucb1x00_mixer_ioctl(inode, file, cmd, arg); ++ } ++ ++ return put_user(val, (int *)arg); ++} ++ ++static int ucb1x00_audio_open(struct inode *inode, struct file *file) ++{ ++ struct ucb1x00_audio *ucba; ++ ++ ucba = list_entry(file->f_op, struct ucb1x00_audio, fops); ++ ++ return sa1100_audio_attach(inode, file, &ucba->state); ++} ++ ++static struct ucb1x00_audio *ucb1x00_audio_alloc(struct ucb1x00 *ucb) ++{ ++ struct ucb1x00_audio *ucba; ++ ++ ucba = kmalloc(sizeof(*ucba), GFP_KERNEL); ++ if (ucba) { ++ memset(ucba, 0, sizeof(*ucba)); ++ ++ ucba->magic = MAGIC; ++ ucba->ucb = ucb; ++ ucba->fops.owner = THIS_MODULE; ++ ucba->fops.open = ucb1x00_audio_open; ++ ucba->mops.owner = THIS_MODULE; ++ ucba->mops.ioctl = ucb1x00_mixer_ioctl; ++ ucba->state.output_stream = &ucba->output_stream; ++ ucba->state.input_stream = &ucba->input_stream; ++ ucba->state.data = ucba; ++ ucba->state.hw_init = ucb1x00_audio_startup; ++ ucba->state.hw_shutdown = ucb1x00_audio_shutdown; ++ ucba->state.client_ioctl = ucb1x00_audio_ioctl; ++ ++ /* There is a bug in the StrongARM causes corrupt MCP data to be sent to ++ * the codec when the FIFOs are empty and writes are made to the OS timer ++ * match register 0. To avoid this we must make sure that data is always ++ * sent to the codec. ++ */ ++ ucba->state.need_tx_for_rx = 1; ++ ++ init_MUTEX(&ucba->state.sem); ++ ucba->rate = 8000; ++ } ++ return ucba; ++} ++ ++static struct ucb1x00_audio *audio, *telecom; ++ ++static int __init ucb1x00_audio_init(void) ++{ ++ struct ucb1x00 *ucb = ucb1x00_get(); ++ struct ucb1x00_audio *a; ++ ++ if (!ucb) ++ return -ENODEV; ++ ++ a = ucb1x00_audio_alloc(ucb); ++ if (a) { ++ a->state.input_dma = ucb->mcp->dma_audio_rd; ++ a->state.input_id = "UCB1x00 audio in"; ++ a->state.output_dma = ucb->mcp->dma_audio_wr; ++ a->state.output_id = "UCB1x00 audio out"; ++ a->dev_id = register_sound_dsp(&a->fops, -1); ++ a->mix_id = register_sound_mixer(&a->mops, -1); ++ a->ctrl_a = 0; ++ a->ctrl_b = UCB_AC_B_IN_ENA|UCB_AC_B_OUT_ENA; ++ audio = a; ++ } ++ ++ a = ucb1x00_audio_alloc(ucb); ++ if (a) { ++#if 0 ++ a->daa_oh_bit = UCB_IO_8; ++ ++ ucb1x00_enable(ucb); ++ ucb1x00_io_write(ucb, a->daa_oh_bit, 0); ++ ucb1x00_io_set_dir(ucb, UCB_IO_7 | UCB_IO_6, a->daa_oh_bit); ++ ucb1x00_disable(ucb); ++#endif ++ ++ a->telecom = 1; ++ a->state.input_dma = ucb->mcp->dma_telco_rd; ++ a->state.input_id = "UCB1x00 telco in"; ++ a->state.output_dma = ucb->mcp->dma_telco_wr; ++ a->state.output_id = "UCB1x00 telco out"; ++ a->dev_id = register_sound_dsp(&a->fops, -1); ++ a->mix_id = register_sound_mixer(&a->mops, -1); ++ a->ctrl_a = 0; ++ a->ctrl_b = UCB_TC_B_IN_ENA|UCB_TC_B_OUT_ENA; ++ telecom = a; ++ } ++ ++ return 0; ++} ++ ++static void __exit ucb1x00_audio_exit(void) ++{ ++ unregister_sound_dsp(telecom->dev_id); ++ unregister_sound_dsp(audio->dev_id); ++ unregister_sound_mixer(telecom->mix_id); ++ unregister_sound_mixer(audio->mix_id); ++} ++ ++module_init(ucb1x00_audio_init); ++module_exit(ucb1x00_audio_exit); ++ ++MODULE_AUTHOR("Russell King "); ++MODULE_DESCRIPTION("UCB1x00 telecom/audio driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/misc/ucb1x00-core.c linux-2.4.26-vrs1/drivers/misc/ucb1x00-core.c +--- linux-2.4.26/drivers/misc/ucb1x00-core.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/misc/ucb1x00-core.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,651 @@ ++/* ++ * linux/drivers/misc/ucb1x00-core.c ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * 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. ++ * ++ * The UCB1x00 core driver provides basic services for handling IO, ++ * the ADC, interrupts, and accessing registers. It is designed ++ * such that everything goes through this layer, thereby providing ++ * a consistent locking methodology, as well as allowing the drivers ++ * to be used on other non-MCP-enabled hardware platforms. ++ * ++ * Note that all locks are private to this file. Nothing else may ++ * touch them. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "ucb1x00.h" ++ ++/** ++ * ucb1x00_io_set_dir - set IO direction ++ * @ucb: UCB1x00 structure describing chip ++ * @in: bitfield of IO pins to be set as inputs ++ * @out: bitfield of IO pins to be set as outputs ++ * ++ * Set the IO direction of the ten general purpose IO pins on ++ * the UCB1x00 chip. The @in bitfield has priority over the ++ * @out bitfield, in that if you specify a pin as both input ++ * and output, it will end up as an input. ++ * ++ * ucb1x00_enable must have been called to enable the comms ++ * before using this function. ++ * ++ * This function takes a spinlock, disabling interrupts. ++ */ ++void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int in, unsigned int out) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ucb->io_lock, flags); ++ ucb->io_dir |= out; ++ ucb->io_dir &= ~in; ++ ++ ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); ++ spin_unlock_irqrestore(&ucb->io_lock, flags); ++} ++ ++/** ++ * ucb1x00_io_write - set or clear IO outputs ++ * @ucb: UCB1x00 structure describing chip ++ * @set: bitfield of IO pins to set to logic '1' ++ * @clear: bitfield of IO pins to set to logic '0' ++ * ++ * Set the IO output state of the specified IO pins. The value ++ * is retained if the pins are subsequently configured as inputs. ++ * The @clear bitfield has priority over the @set bitfield - ++ * outputs will be cleared. ++ * ++ * ucb1x00_enable must have been called to enable the comms ++ * before using this function. ++ * ++ * This function takes a spinlock, disabling interrupts. ++ */ ++void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ucb->io_lock, flags); ++ ucb->io_out |= set; ++ ucb->io_out &= ~clear; ++ ++ ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); ++ spin_unlock_irqrestore(&ucb->io_lock, flags); ++} ++ ++/** ++ * ucb1x00_io_read - read the current state of the IO pins ++ * @ucb: UCB1x00 structure describing chip ++ * ++ * Return a bitfield describing the logic state of the ten ++ * general purpose IO pins. ++ * ++ * ucb1x00_enable must have been called to enable the comms ++ * before using this function. ++ * ++ * This function does not take any semaphores or spinlocks. ++ */ ++unsigned int ucb1x00_io_read(struct ucb1x00 *ucb) ++{ ++ return ucb1x00_reg_read(ucb, UCB_IO_DATA); ++} ++ ++/* ++ * UCB1300 data sheet says we must: ++ * 1. enable ADC => 5us (including reference startup time) ++ * 2. select input => 51*tsibclk => 4.3us ++ * 3. start conversion => 102*tsibclk => 8.5us ++ * (tsibclk = 1/11981000) ++ * Period between SIB 128-bit frames = 10.7us ++ */ ++ ++/** ++ * ucb1x00_adc_enable - enable the ADC converter ++ * @ucb: UCB1x00 structure describing chip ++ * ++ * Enable the ucb1x00 and ADC converter on the UCB1x00 for use. ++ * Any code wishing to use the ADC converter must call this ++ * function prior to using it. ++ * ++ * This function takes the ADC semaphore to prevent two or more ++ * concurrent uses, and therefore may sleep. As a result, it ++ * can only be called from process context, not interrupt ++ * context. ++ * ++ * You should release the ADC as soon as possible using ++ * ucb1x00_adc_disable. ++ */ ++void ucb1x00_adc_enable(struct ucb1x00 *ucb) ++{ ++ down(&ucb->adc_sem); ++ ++ ucb->adc_cr |= UCB_ADC_ENA; ++ ++ ucb1x00_enable(ucb); ++ ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); ++} ++ ++/** ++ * ucb1x00_adc_read - read the specified ADC channel ++ * @ucb: UCB1x00 structure describing chip ++ * @adc_channel: ADC channel mask ++ * @sync: wait for syncronisation pulse. ++ * ++ * Start an ADC conversion and wait for the result. Note that ++ * synchronised ADC conversions (via the ADCSYNC pin) must wait ++ * until the trigger is asserted and the conversion is finished. ++ * ++ * This function currently spins waiting for the conversion to ++ * complete (2 frames max without sync). ++ * ++ * If called for a synchronised ADC conversion, it may sleep ++ * with the ADC semaphore held. ++ */ ++unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) ++{ ++ unsigned int val; ++ ++ if (sync) ++ adc_channel |= UCB_ADC_SYNC_ENA; ++ ++ ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel); ++ ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START); ++ ++ for (;;) { ++ val = ucb1x00_reg_read(ucb, UCB_ADC_DATA); ++ if (val & UCB_ADC_DAT_VAL) ++ break; ++ /* yield to other processes */ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ } ++ ++ return UCB_ADC_DAT(val); ++} ++ ++/** ++ * ucb1x00_adc_disable - disable the ADC converter ++ * @ucb: UCB1x00 structure describing chip ++ * ++ * Disable the ADC converter and release the ADC semaphore. ++ */ ++void ucb1x00_adc_disable(struct ucb1x00 *ucb) ++{ ++ ucb->adc_cr &= ~UCB_ADC_ENA; ++ ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); ++ ucb1x00_disable(ucb); ++ ++ up(&ucb->adc_sem); ++} ++ ++#ifdef CONFIG_PM ++static int ucb1x00_pm (struct pm_dev *dev, pm_request_t rqst, void *data) ++{ ++ struct ucb1x00 *ucb = (struct ucb1x00 *)dev->data; ++ unsigned int isr; ++ ++ if (rqst == PM_RESUME) { ++ ucb1x00_enable(ucb); ++ isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); ++ ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); ++ ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); ++ ucb1x00_disable(ucb); ++ } ++ ++ return 0; ++} ++#endif ++ ++/* ++ * UCB1x00 Interrupt handling. ++ * ++ * The UCB1x00 can generate interrupts when the SIBCLK is stopped. ++ * Since we need to read an internal register, we must re-enable ++ * SIBCLK to talk to the chip. We leave the clock running until ++ * we have finished processing all interrupts from the chip. ++ */ ++static void ucb1x00_irq(int irqnr, void *devid, struct pt_regs *regs) ++{ ++ struct ucb1x00 *ucb = devid; ++ struct ucb1x00_irq *irq; ++ unsigned int isr, i; ++ ++ ucb1x00_enable(ucb); ++ isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); ++ ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); ++ ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); ++ ++ for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++) ++ if (isr & 1 && irq->fn) ++ irq->fn(i, irq->devid); ++ ucb1x00_disable(ucb); ++} ++ ++/** ++ * ucb1x00_hook_irq - hook a UCB1x00 interrupt ++ * @ucb: UCB1x00 structure describing chip ++ * @idx: interrupt index ++ * @fn: function to call when interrupt is triggered ++ * @devid: device id to pass to interrupt handler ++ * ++ * Hook the specified interrupt. You can only register one handler ++ * for each interrupt source. The interrupt source is not enabled ++ * by this function; use ucb1x00_enable_irq instead. ++ * ++ * Interrupt handlers will be called with other interrupts enabled. ++ * ++ * Returns zero on success, or one of the following errors: ++ * -EINVAL if the interrupt index is invalid ++ * -EBUSY if the interrupt has already been hooked ++ */ ++int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid) ++{ ++ struct ucb1x00_irq *irq; ++ int ret = -EINVAL; ++ ++ if (idx < 16) { ++ irq = ucb->irq_handler + idx; ++ ret = -EBUSY; ++ ++ spin_lock_irq(&ucb->lock); ++ if (irq->fn == NULL) { ++ irq->devid = devid; ++ irq->fn = fn; ++ ret = 0; ++ } ++ spin_unlock_irq(&ucb->lock); ++ } ++ return ret; ++} ++ ++/** ++ * ucb1x00_enable_irq - enable an UCB1x00 interrupt source ++ * @ucb: UCB1x00 structure describing chip ++ * @idx: interrupt index ++ * @edges: interrupt edges to enable ++ * ++ * Enable the specified interrupt to trigger on %UCB_RISING, ++ * %UCB_FALLING or both edges. The interrupt should have been ++ * hooked by ucb1x00_hook_irq. ++ */ ++void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) ++{ ++ unsigned long flags; ++ ++ if (idx < 16) { ++ spin_lock_irqsave(&ucb->lock, flags); ++ ++ ucb1x00_enable(ucb); ++ if (edges & UCB_RISING) { ++ ucb->irq_ris_enbl |= 1 << idx; ++ ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); ++ } ++ if (edges & UCB_FALLING) { ++ ucb->irq_fal_enbl |= 1 << idx; ++ ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); ++ } ++ ucb1x00_disable(ucb); ++ spin_unlock_irqrestore(&ucb->lock, flags); ++ } ++} ++ ++/** ++ * ucb1x00_disable_irq - disable an UCB1x00 interrupt source ++ * @ucb: UCB1x00 structure describing chip ++ * @edges: interrupt edges to disable ++ * ++ * Disable the specified interrupt triggering on the specified ++ * (%UCB_RISING, %UCB_FALLING or both) edges. ++ */ ++void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) ++{ ++ unsigned long flags; ++ ++ if (idx < 16) { ++ spin_lock_irqsave(&ucb->lock, flags); ++ ++ ucb1x00_enable(ucb); ++ if (edges & UCB_RISING) { ++ ucb->irq_ris_enbl &= ~(1 << idx); ++ ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); ++ } ++ if (edges & UCB_FALLING) { ++ ucb->irq_fal_enbl &= ~(1 << idx); ++ ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); ++ } ++ ucb1x00_disable(ucb); ++ spin_unlock_irqrestore(&ucb->lock, flags); ++ } ++} ++ ++/** ++ * ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt ++ * @ucb: UCB1x00 structure describing chip ++ * @idx: interrupt index ++ * @devid: device id. ++ * ++ * Disable the interrupt source and remove the handler. devid must ++ * match the devid passed when hooking the interrupt. ++ * ++ * Returns zero on success, or one of the following errors: ++ * -EINVAL if the interrupt index is invalid ++ * -ENOENT if devid does not match ++ */ ++int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid) ++{ ++ struct ucb1x00_irq *irq; ++ int ret; ++ ++ if (idx >= 16) ++ goto bad; ++ ++ irq = ucb->irq_handler + idx; ++ ret = -ENOENT; ++ ++ spin_lock_irq(&ucb->lock); ++ if (irq->devid == devid) { ++ ucb->irq_ris_enbl &= ~(1 << idx); ++ ucb->irq_fal_enbl &= ~(1 << idx); ++ ++ ucb1x00_enable(ucb); ++ ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); ++ ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); ++ ucb1x00_disable(ucb); ++ ++ irq->fn = NULL; ++ irq->devid = NULL; ++ ret = 0; ++ } ++ spin_unlock_irq(&ucb->lock); ++ return ret; ++ ++bad: ++ printk(KERN_ERR "%s: freeing bad irq %d\n", __FUNCTION__, idx); ++ return -EINVAL; ++} ++ ++/* ++ * Try to probe our interrupt, rather than relying on lots of ++ * hard-coded machine dependencies. For reference, the expected ++ * IRQ mappings are: ++ * ++ * Machine Default IRQ ++ * adsbitsy IRQ_GPCIN4 ++ * cerf IRQ_GPIO_UCB1200_IRQ ++ * flexanet IRQ_GPIO_GUI ++ * freebird IRQ_GPIO_FREEBIRD_UCB1300_IRQ ++ * graphicsclient IRQ_GRAPHICSCLIENT_UCB1200 ++ * graphicsmaster IRQ_GRAPHICSMASTER_UCB1200 ++ * lart LART_IRQ_UCB1200 ++ * omnimeter IRQ_GPIO23 ++ * pfs168 IRQ_GPIO_UCB1300_IRQ ++ * simpad IRQ_GPIO_UCB1300_IRQ ++ * shannon SHANNON_IRQ_GPIO_IRQ_CODEC ++ * yopy IRQ_GPIO_UCB1200_IRQ ++ */ ++static int __init ucb1x00_detect_irq(struct ucb1x00 *ucb) ++{ ++ unsigned long mask; ++ ++ mask = probe_irq_on(); ++ if (!mask) ++ return NO_IRQ; ++ ++ /* ++ * Enable the ADC interrupt. ++ */ ++ ucb1x00_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC); ++ ucb1x00_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC); ++ ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); ++ ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); ++ ++ /* ++ * Cause an ADC interrupt. ++ */ ++ ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); ++ ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); ++ ++ /* ++ * Wait for the conversion to complete. ++ */ ++ while ((ucb1x00_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VAL) == 0); ++ ucb1x00_reg_write(ucb, UCB_ADC_CR, 0); ++ ++ /* ++ * Disable and clear interrupt. ++ */ ++ ucb1x00_reg_write(ucb, UCB_IE_RIS, 0); ++ ucb1x00_reg_write(ucb, UCB_IE_FAL, 0); ++ ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); ++ ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); ++ ++ /* ++ * Read triggered interrupt. ++ */ ++ return probe_irq_off(mask); ++} ++ ++/* ++ * This configures the UCB1x00 layer depending on the machine type ++ * we're running on. The UCB1x00 drivers should not contain any ++ * machine dependencies. ++ * ++ * We can get rid of some of these dependencies by using existing ++ * facilities provided by the kernel - namely IRQ probing. The ++ * machine specific files are expected to setup the IRQ levels on ++ * initialisation. With any luck, we'll get rid of all the ++ * machine dependencies here. ++ */ ++static int __init ucb1x00_configure(struct ucb1x00 *ucb) ++{ ++ unsigned int irq_gpio_pin = 0; ++ int irq, default_irq = NO_IRQ; ++ ++ if (machine_is_adsbitsy()) ++ default_irq = IRQ_GPCIN4; ++ ++// if (machine_is_assabet()) ++// default_irq = IRQ_GPIO23; ++ ++#ifdef CONFIG_SA1100_CERF ++ if (machine_is_cerf()) ++ default_irq = IRQ_GPIO_UCB1200_IRQ; ++#endif ++#ifdef CONFIG_SA1100_FREEBIRD ++ if (machine_is_freebird()) ++ default_irq = IRQ_GPIO_FREEBIRD_UCB1300_IRQ; ++#endif ++#if defined(CONFIG_SA1100_GRAPHICSCLIENT) ++// if (machine_is_graphicsclient()) ++// default_irq = IRQ_GRAPHICSCLIENT_UCB1200; ++#endif ++#if defined(CONFIG_SA1100_GRAPICSMASTER) ++ if (machine_is_graphicsmaster()) ++ default_irq = IRQ_GRAPHICSMASTER_UCB1200; ++#endif ++#ifdef CONFIG_SA1100_LART ++ if (machine_is_lart()) { ++ default_irq = LART_IRQ_UCB1200; ++ irq_gpio_pin = LART_GPIO_UCB1200; ++ } ++#endif ++ if (machine_is_omnimeter()) ++ default_irq = IRQ_GPIO23; ++ ++#ifdef CONFIG_SA1100_PFS168 ++ if (machine_is_pfs168()) ++ default_irq = IRQ_GPIO_UCB1300_IRQ; ++#endif ++#ifdef CONFIG_SA1100_SIMPAD ++ if (machine_is_simpad()) ++ default_irq = IRQ_GPIO_UCB1300_IRQ; ++#endif ++#ifdef CONFIG_SA1100_SIMPUTER ++ if (machine_is_simputer()) { ++ default_irq = IRQ_GPIO_UCB1300_IRQ; ++ irq_gpio_pin = GPIO_UCB1300_IRQ; ++ } ++#endif ++ if (machine_is_shannon()) ++ default_irq = SHANNON_IRQ_GPIO_IRQ_CODEC; ++#ifdef CONFIG_SA1100_YOPY ++ if (machine_is_yopy()) ++ default_irq = IRQ_GPIO_UCB1200_IRQ; ++#endif ++#ifdef CONFIG_SA1100_ACCELENT ++ if (machine_is_accelent_sa()) { ++ ucb->irq = IRQ_GPIO_UCB1200_IRQ; ++ irq_gpio_pin = GPIO_UCB1200_IRQ; ++ } ++#endif ++ ++ /* ++ * Eventually, this will disappear. ++ */ ++ if (irq_gpio_pin) ++ set_GPIO_IRQ_edge(irq_gpio_pin, GPIO_RISING_EDGE); ++ ++ irq = ucb1x00_detect_irq(ucb); ++ if (irq != NO_IRQ) { ++ if (default_irq != NO_IRQ && irq != default_irq) ++ printk(KERN_ERR "UCB1x00: probed IRQ%d != default IRQ%d\n", ++ irq, default_irq); ++ if (irq == default_irq) ++ printk(KERN_ERR "UCB1x00: probed IRQ%d correctly. " ++ "Please remove machine dependencies from " ++ "ucb1x00-core.c\n", irq); ++ ucb->irq = irq; ++ } else { ++ printk(KERN_ERR "UCB1x00: IRQ probe failed, using IRQ%d\n", ++ default_irq); ++ ucb->irq = default_irq; ++ } ++ ++ return ucb->irq == NO_IRQ ? -ENODEV : 0; ++} ++ ++struct ucb1x00 *my_ucb; ++ ++/** ++ * ucb1x00_get - get the UCB1x00 structure describing a chip ++ * @ucb: UCB1x00 structure describing chip ++ * ++ * Return the UCB1x00 structure describing a chip. ++ * ++ * FIXME: Currently very noddy indeed, which currently doesn't ++ * matter since we only support one chip. ++ */ ++struct ucb1x00 *ucb1x00_get(void) ++{ ++ return my_ucb; ++} ++ ++static int __init ucb1x00_init(void) ++{ ++ struct mcp *mcp; ++ unsigned int id; ++ int ret = -ENODEV; ++ ++ mcp = mcp_get(); ++ if (!mcp) ++ goto no_mcp; ++ ++ mcp_enable(mcp); ++ id = mcp_reg_read(mcp, UCB_ID); ++ ++ if (id != UCB_ID_1200 && id != UCB_ID_1300) { ++ printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id); ++ goto out; ++ } ++ ++ my_ucb = kmalloc(sizeof(struct ucb1x00), GFP_KERNEL); ++ ret = -ENOMEM; ++ if (!my_ucb) ++ goto out; ++ ++ if (machine_is_shannon()) { ++ /* reset the codec */ ++ GPDR |= SHANNON_GPIO_CODEC_RESET; ++ GPCR = SHANNON_GPIO_CODEC_RESET; ++ GPSR = SHANNON_GPIO_CODEC_RESET; ++ ++ } ++ ++ memset(my_ucb, 0, sizeof(struct ucb1x00)); ++ ++ spin_lock_init(&my_ucb->lock); ++ spin_lock_init(&my_ucb->io_lock); ++ sema_init(&my_ucb->adc_sem, 1); ++ ++ my_ucb->id = id; ++ my_ucb->mcp = mcp; ++ ++ ret = ucb1x00_configure(my_ucb); ++ if (ret) ++ goto out; ++ ++ ret = request_irq(my_ucb->irq, ucb1x00_irq, 0, "UCB1x00", my_ucb); ++ if (ret) { ++ printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n", ++ my_ucb->irq, ret); ++ kfree(my_ucb); ++ my_ucb = NULL; ++ goto out; ++ } ++ ++#ifdef CONFIG_PM ++ my_ucb->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ucb1x00_pm); ++ if (my_ucb->pmdev == NULL) ++ printk("ucb1x00: unable to register in PM.\n"); ++ else ++ my_ucb->pmdev->data = my_ucb; ++#endif ++ ++out: ++ mcp_disable(mcp); ++no_mcp: ++ return ret; ++} ++ ++static void __exit ucb1x00_exit(void) ++{ ++ free_irq(my_ucb->irq, my_ucb); ++ kfree(my_ucb); ++} ++ ++module_init(ucb1x00_init); ++module_exit(ucb1x00_exit); ++ ++EXPORT_SYMBOL(ucb1x00_get); ++ ++EXPORT_SYMBOL(ucb1x00_io_set_dir); ++EXPORT_SYMBOL(ucb1x00_io_write); ++EXPORT_SYMBOL(ucb1x00_io_read); ++ ++EXPORT_SYMBOL(ucb1x00_adc_enable); ++EXPORT_SYMBOL(ucb1x00_adc_read); ++EXPORT_SYMBOL(ucb1x00_adc_disable); ++ ++EXPORT_SYMBOL(ucb1x00_hook_irq); ++EXPORT_SYMBOL(ucb1x00_free_irq); ++EXPORT_SYMBOL(ucb1x00_enable_irq); ++EXPORT_SYMBOL(ucb1x00_disable_irq); ++ ++MODULE_AUTHOR("Russell King "); ++MODULE_DESCRIPTION("UCB1x00 core driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/misc/ucb1x00-ts.c linux-2.4.26-vrs1/drivers/misc/ucb1x00-ts.c +--- linux-2.4.26/drivers/misc/ucb1x00-ts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/misc/ucb1x00-ts.c 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,664 @@ ++/* ++ * linux/drivers/misc/ucb1x00-ts.c ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * 21-Jan-2002 : ++ * ++ * Added support for synchronous A/D mode. This mode is useful to ++ * avoid noise induced in the touchpanel by the LCD, provided that ++ * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin. ++ * It is important to note that the signal connected to the ADCSYNC ++ * pin should provide pulses even when the LCD is blanked, otherwise ++ * a pen touch needed to unblank the LCD will never be read. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "ucb1x00.h" ++ ++/* ++ * Define this if you want the UCB1x00 stuff to talk to the input layer ++ */ ++#undef USE_INPUT ++ ++#ifndef USE_INPUT ++ ++#include ++#include ++#include ++ ++/* ++ * This structure is nonsense - millisecs is not very useful ++ * since the field size is too small. Also, we SHOULD NOT ++ * be exposing jiffies to user space directly. ++ */ ++struct ts_event { ++ u16 pressure; ++ u16 x; ++ u16 y; ++ u16 pad; ++ struct timeval stamp; ++}; ++ ++#define NR_EVENTS 16 ++ ++#else ++ ++#include ++ ++#endif ++ ++struct ucb1x00_ts { ++#ifdef USE_INPUT ++ struct input_dev idev; ++#endif ++ struct ucb1x00 *ucb; ++#ifdef CONFIG_PM ++ struct pm_dev *pmdev; ++#endif ++ ++ wait_queue_head_t irq_wait; ++ struct semaphore sem; ++ struct completion init_exit; ++ struct task_struct *rtask; ++ int use_count; ++ u16 x_res; ++ u16 y_res; ++ ++#ifndef USE_INPUT ++ struct fasync_struct *fasync; ++ wait_queue_head_t read_wait; ++ u8 evt_head; ++ u8 evt_tail; ++ struct ts_event events[NR_EVENTS]; ++#endif ++ int restart:1; ++ int adcsync:1; ++}; ++ ++static struct ucb1x00_ts ucbts; ++static int adcsync = UCB_NOSYNC; ++ ++static int ucb1x00_ts_startup(struct ucb1x00_ts *ts); ++static void ucb1x00_ts_shutdown(struct ucb1x00_ts *ts); ++ ++#ifndef USE_INPUT ++ ++#define ucb1x00_ts_evt_pending(ts) ((volatile u8)(ts)->evt_head != (ts)->evt_tail) ++#define ucb1x00_ts_evt_get(ts) ((ts)->events + (ts)->evt_tail) ++#define ucb1x00_ts_evt_pull(ts) ((ts)->evt_tail = ((ts)->evt_tail + 1) & (NR_EVENTS - 1)) ++#define ucb1x00_ts_evt_clear(ts) ((ts)->evt_head = (ts)->evt_tail = 0) ++ ++static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) ++{ ++ int next_head; ++ ++ next_head = (ts->evt_head + 1) & (NR_EVENTS - 1); ++ if (next_head != ts->evt_tail) { ++ ts->events[ts->evt_head].pressure = pressure; ++ ts->events[ts->evt_head].x = x; ++ ts->events[ts->evt_head].y = y; ++ do_gettimeofday(&ts->events[ts->evt_head].stamp); ++ ts->evt_head = next_head; ++ ++ if (ts->fasync) ++ kill_fasync(&ts->fasync, SIGIO, POLL_IN); ++ wake_up_interruptible(&ts->read_wait); ++ } ++} ++ ++static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_ts_evt_add(ts, 0, 0, 0); ++} ++ ++/* ++ * User space driver interface. ++ */ ++static ssize_t ++ucb1x00_ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ struct ucb1x00_ts *ts = filp->private_data; ++ char *ptr = buffer; ++ int err = 0; ++ ++ add_wait_queue(&ts->read_wait, &wait); ++ while (count >= sizeof(struct ts_event)) { ++ err = -ERESTARTSYS; ++ if (signal_pending(current)) ++ break; ++ ++ if (ucb1x00_ts_evt_pending(ts)) { ++ struct ts_event *evt = ucb1x00_ts_evt_get(ts); ++ ++ err = copy_to_user(ptr, evt, sizeof(struct ts_event)); ++ ucb1x00_ts_evt_pull(ts); ++ ++ if (err) ++ break; ++ ++ ptr += sizeof(struct ts_event); ++ count -= sizeof(struct ts_event); ++ continue; ++ } ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ err = -EAGAIN; ++ if (filp->f_flags & O_NONBLOCK) ++ break; ++ schedule(); ++ } ++ current->state = TASK_RUNNING; ++ remove_wait_queue(&ts->read_wait, &wait); ++ ++ return ptr == buffer ? err : ptr - buffer; ++} ++ ++static unsigned int ucb1x00_ts_poll(struct file *filp, poll_table *wait) ++{ ++ struct ucb1x00_ts *ts = filp->private_data; ++ int ret = 0; ++ ++ poll_wait(filp, &ts->read_wait, wait); ++ if (ucb1x00_ts_evt_pending(ts)) ++ ret = POLLIN | POLLRDNORM; ++ ++ return ret; ++} ++ ++static int ucb1x00_ts_fasync(int fd, struct file *filp, int on) ++{ ++ struct ucb1x00_ts *ts = filp->private_data; ++ ++ return fasync_helper(fd, filp, on, &ts->fasync); ++} ++ ++static int ucb1x00_ts_open(struct inode *inode, struct file *filp) ++{ ++ struct ucb1x00_ts *ts = &ucbts; ++ int ret = 0; ++ ++ ret = ucb1x00_ts_startup(ts); ++ if (ret == 0) ++ filp->private_data = ts; ++ ++ return ret; ++} ++ ++/* ++ * Release touchscreen resources. Disable IRQs. ++ */ ++static int ucb1x00_ts_release(struct inode *inode, struct file *filp) ++{ ++ struct ucb1x00_ts *ts = filp->private_data; ++ ++ down(&ts->sem); ++ ucb1x00_ts_fasync(-1, filp, 0); ++ ucb1x00_ts_shutdown(ts); ++ up(&ts->sem); ++ ++ return 0; ++} ++ ++static struct file_operations ucb1x00_fops = { ++ owner: THIS_MODULE, ++ read: ucb1x00_ts_read, ++ poll: ucb1x00_ts_poll, ++ open: ucb1x00_ts_open, ++ release: ucb1x00_ts_release, ++ fasync: ucb1x00_ts_fasync, ++}; ++ ++/* ++ * The official UCB1x00 touchscreen is a miscdevice: ++ * 10 char Non-serial mice, misc features ++ * 14 = /dev/touchscreen/ucb1x00 UCB 1x00 touchscreen ++ */ ++static struct miscdevice ucb1x00_ts_dev = { ++ minor: 14, ++ name: "touchscreen/ucb1x00", ++ fops: &ucb1x00_fops, ++}; ++ ++static inline int ucb1x00_ts_register(struct ucb1x00_ts *ts) ++{ ++ init_waitqueue_head(&ts->read_wait); ++ return misc_register(&ucb1x00_ts_dev); ++} ++ ++static inline void ucb1x00_ts_deregister(struct ucb1x00_ts *ts) ++{ ++ misc_deregister(&ucb1x00_ts_dev); ++} ++ ++#else ++ ++#define ucb1x00_ts_evt_clear(ts) do { } while (0) ++ ++static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) ++{ ++ input_report_abs(&ts->idev, ABS_X, x); ++ input_report_abs(&ts->idev, ABS_Y, y); ++ input_report_abs(&ts->idev, ABS_PRESSURE, pressure); ++} ++ ++static int ucb1x00_ts_open(struct input_dev *idev) ++{ ++ struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; ++ ++ return ucb1x00_ts_startup(ts); ++} ++ ++static void ucb1x00_ts_close(struct input_dev *idev) ++{ ++ struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; ++ ++ down(&ts->sem); ++ ucb1x00_ts_shutdown(ts); ++ up(&ts->sem); ++} ++ ++static inline int ucb1x00_ts_register(struct ucb1x00_ts *ts) ++{ ++ ts->idev.name = "Touchscreen panel"; ++ ts->idev.idproduct = ts->ucb->id; ++ ts->idev.open = ucb1x00_ts_open; ++ ts->idev.close = ucb1x00_ts_close; ++ ++ __set_bit(EV_ABS, ts->idev.evbit); ++ __set_bit(ABS_X, ts->idev.absbit); ++ __set_bit(ABS_Y, ts->idev.absbit); ++ __set_bit(ABS_PRESSURE, ts->idev.absbit); ++ ++ input_register_device(&ts->idev); ++ ++ return 0; ++} ++ ++static inline void ucb1x00_ts_deregister(struct ucb1x00_ts *ts) ++{ ++ input_unregister_device(&ts->idev); ++} ++ ++#endif ++ ++/* ++ * Switch to interrupt mode. ++ */ ++static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | ++ UCB_TS_CR_MODE_INT); ++} ++ ++/* ++ * Switch to pressure mode, and read pressure. We don't need to wait ++ * here, since both plates are being driven. ++ */ ++static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ++ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); ++} ++ ++/* ++ * Switch to X position mode and measure Y plate. We switch the plate ++ * configuration in pressure mode, then switch to position mode. This ++ * gives a faster response time. Even so, we need to wait about 55us ++ * for things to stabilise. ++ */ ++static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); ++ ++ udelay(55); ++ ++ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); ++} ++ ++/* ++ * Switch to Y position mode and measure X plate. We switch the plate ++ * configuration in pressure mode, then switch to position mode. This ++ * gives a faster response time. Even so, we need to wait about 55us ++ * for things to stabilise. ++ */ ++static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); ++ ++ udelay(55); ++ ++ return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); ++} ++ ++/* ++ * Switch to X plate resistance mode. Set MX to ground, PX to ++ * supply. Measure current. ++ */ ++static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); ++} ++ ++/* ++ * Switch to Y plate resistance mode. Set MY to ground, PY to ++ * supply. Measure current. ++ */ ++static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) ++{ ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, ++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | ++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); ++ return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); ++} ++ ++/* ++ * This is a RT kernel thread that handles the ADC accesses ++ * (mainly so we can use semaphores in the UCB1200 core code ++ * to serialise accesses to the ADC). ++ */ ++static int ucb1x00_thread(void *_ts) ++{ ++ struct ucb1x00_ts *ts = _ts; ++ struct task_struct *tsk = current; ++ DECLARE_WAITQUEUE(wait, tsk); ++ int valid; ++ ++ ts->rtask = tsk; ++ ++ daemonize(); ++ reparent_to_init(); ++ strcpy(tsk->comm, "ktsd"); ++ tsk->tty = NULL; ++ /* ++ * We could run as a real-time thread. However, thus far ++ * this doesn't seem to be necessary. ++ */ ++// tsk->policy = SCHED_FIFO; ++// tsk->rt_priority = 1; ++ ++ /* only want to receive SIGKILL */ ++ spin_lock_irq(&tsk->sigmask_lock); ++ siginitsetinv(&tsk->blocked, sigmask(SIGKILL)); ++ recalc_sigpending(tsk); ++ spin_unlock_irq(&tsk->sigmask_lock); ++ ++ complete(&ts->init_exit); ++ ++ valid = 0; ++ ++ add_wait_queue(&ts->irq_wait, &wait); ++ for (;;) { ++ unsigned int x, y, p, val; ++ signed long timeout; ++ ++ ts->restart = 0; ++ ++ ucb1x00_adc_enable(ts->ucb); ++ ++ x = ucb1x00_ts_read_xpos(ts); ++ y = ucb1x00_ts_read_ypos(ts); ++ p = ucb1x00_ts_read_pressure(ts); ++ ++ /* ++ * Switch back to interrupt mode. ++ */ ++ ucb1x00_ts_mode_int(ts); ++ ucb1x00_adc_disable(ts->ucb); ++ ++ set_task_state(tsk, TASK_UNINTERRUPTIBLE); ++ schedule_timeout(HZ / 100); ++ if (signal_pending(tsk)) ++ break; ++ ++ ucb1x00_enable(ts->ucb); ++ val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR); ++ ++ if (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)) { ++ set_task_state(tsk, TASK_INTERRUPTIBLE); ++ ++ ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); ++ ucb1x00_disable(ts->ucb); ++ ++ /* ++ * If we spat out a valid sample set last time, ++ * spit out a "pen off" sample here. ++ */ ++ if (valid) { ++ ucb1x00_ts_event_release(ts); ++ valid = 0; ++ } ++ ++ timeout = MAX_SCHEDULE_TIMEOUT; ++ } else { ++ ucb1x00_disable(ts->ucb); ++ ++ /* ++ * Filtering is policy. Policy belongs in user ++ * space. We therefore leave it to user space ++ * to do any filtering they please. ++ */ ++ if (!ts->restart) { ++ ucb1x00_ts_evt_add(ts, p, x, y); ++ valid = 1; ++ } ++ ++ set_task_state(tsk, TASK_INTERRUPTIBLE); ++ timeout = HZ / 100; ++ } ++ ++ schedule_timeout(timeout); ++ if (signal_pending(tsk)) ++ break; ++ } ++ ++ remove_wait_queue(&ts->irq_wait, &wait); ++ ++ ts->rtask = NULL; ++ ucb1x00_ts_evt_clear(ts); ++ complete_and_exit(&ts->init_exit, 0); ++} ++ ++/* ++ * We only detect touch screen _touches_ with this interrupt ++ * handler, and even then we just schedule our task. ++ */ ++static void ucb1x00_ts_irq(int idx, void *id) ++{ ++ struct ucb1x00_ts *ts = id; ++ ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); ++ wake_up(&ts->irq_wait); ++} ++ ++static int ucb1x00_ts_startup(struct ucb1x00_ts *ts) ++{ ++ int ret = 0; ++ ++ if (down_interruptible(&ts->sem)) ++ return -EINTR; ++ ++ if (ts->use_count++ != 0) ++ goto out; ++ ++ if (ts->rtask) ++ panic("ucb1x00: rtask running?"); ++ ++ init_waitqueue_head(&ts->irq_wait); ++ ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); ++ if (ret < 0) ++ goto out; ++ ++ /* ++ * If we do this at all, we should allow the user to ++ * measure and read the X and Y resistance at any time. ++ */ ++ ucb1x00_adc_enable(ts->ucb); ++ ts->x_res = ucb1x00_ts_read_xres(ts); ++ ts->y_res = ucb1x00_ts_read_yres(ts); ++ ucb1x00_adc_disable(ts->ucb); ++ ++ init_completion(&ts->init_exit); ++ ret = kernel_thread(ucb1x00_thread, ts, 0); ++ if (ret >= 0) { ++ wait_for_completion(&ts->init_exit); ++ ret = 0; ++ } else { ++ ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); ++ } ++ ++ out: ++ if (ret) ++ ts->use_count--; ++ up(&ts->sem); ++ return ret; ++} ++ ++/* ++ * Release touchscreen resources. Disable IRQs. ++ */ ++static void ucb1x00_ts_shutdown(struct ucb1x00_ts *ts) ++{ ++ if (--ts->use_count == 0) { ++ if (ts->rtask) { ++ send_sig(SIGKILL, ts->rtask, 1); ++ wait_for_completion(&ts->init_exit); ++ } ++ ++ ucb1x00_enable(ts->ucb); ++ ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); ++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); ++ ucb1x00_disable(ts->ucb); ++ } ++} ++ ++#ifdef CONFIG_PM ++static int ucb1x00_ts_pm (struct pm_dev *dev, pm_request_t rqst, void *data) ++{ ++ struct ucb1x00_ts *ts = (struct ucb1x00_ts *) (dev->data); ++ ++ if (rqst == PM_RESUME && ts->rtask != NULL) { ++ /* ++ * Restart the TS thread to ensure the ++ * TS interrupt mode is set up again ++ * after sleep. ++ */ ++ ts->restart = 1; ++ wake_up(&ts->irq_wait); ++ } ++ return 0; ++} ++#endif ++ ++ ++/* ++ * Initialisation. ++ */ ++static int __init ucb1x00_ts_init(void) ++{ ++ struct ucb1x00_ts *ts = &ucbts; ++ ++ ts->ucb = ucb1x00_get(); ++ if (!ts->ucb) ++ return -ENODEV; ++ ++ ts->adcsync = adcsync; ++ init_MUTEX(&ts->sem); ++ ++#ifdef CONFIG_PM ++ ts->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ucb1x00_ts_pm); ++ if (ts->pmdev == NULL) ++ printk("ucb1x00_ts: unable to register in PM.\n"); ++ else ++ ts->pmdev->data = ts; ++#endif ++ return ucb1x00_ts_register(ts); ++} ++ ++static void __exit ucb1x00_ts_exit(void) ++{ ++ struct ucb1x00_ts *ts = &ucbts; ++ ++ ucb1x00_ts_deregister(ts); ++ ++#ifdef CONFIG_PM ++ if (ts->pmdev) ++ pm_unregister(ts->pmdev); ++#endif ++} ++ ++#ifndef MODULE ++ ++/* ++ * Parse kernel command-line options. ++ * ++ * syntax : ucbts=[sync|nosync],... ++ */ ++static int __init ucb1x00_ts_setup(char *str) ++{ ++ char *p; ++ ++ while ((p = strsep(&str, ",")) != NULL) { ++ if (strcmp(p, "sync") == 0) ++ adcsync = UCB_SYNC; ++ } ++ ++ return 1; ++} ++ ++__setup("ucbts=", ucb1x00_ts_setup); ++ ++#else ++ ++MODULE_PARM(adcsync, "i"); ++MODULE_PARM_DESC(adcsync, "Enable use of ADCSYNC signal"); ++ ++#endif ++ ++module_init(ucb1x00_ts_init); ++module_exit(ucb1x00_ts_exit); ++ ++MODULE_AUTHOR("Russell King "); ++MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/misc/ucb1x00.h linux-2.4.26-vrs1/drivers/misc/ucb1x00.h +--- linux-2.4.26/drivers/misc/ucb1x00.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/misc/ucb1x00.h 2004-01-14 21:32:25.000000000 +0000 +@@ -0,0 +1,232 @@ ++/* ++ * linux/drivers/misc/ucb1x00.h ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * 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. ++ */ ++#ifndef UCB1200_H ++#define UCB1200_H ++ ++#define UCB_IO_DATA 0x00 ++#define UCB_IO_DIR 0x01 ++ ++#define UCB_IO_0 (1 << 0) ++#define UCB_IO_1 (1 << 1) ++#define UCB_IO_2 (1 << 2) ++#define UCB_IO_3 (1 << 3) ++#define UCB_IO_4 (1 << 4) ++#define UCB_IO_5 (1 << 5) ++#define UCB_IO_6 (1 << 6) ++#define UCB_IO_7 (1 << 7) ++#define UCB_IO_8 (1 << 8) ++#define UCB_IO_9 (1 << 9) ++ ++#define UCB_IE_RIS 0x02 ++#define UCB_IE_FAL 0x03 ++#define UCB_IE_STATUS 0x04 ++#define UCB_IE_CLEAR 0x04 ++#define UCB_IE_ADC (1 << 11) ++#define UCB_IE_TSPX (1 << 12) ++#define UCB_IE_TSMX (1 << 13) ++#define UCB_IE_TCLIP (1 << 14) ++#define UCB_IE_ACLIP (1 << 15) ++ ++#define UCB_IRQ_TSPX 12 ++ ++#define UCB_TC_A 0x05 ++#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ ++#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ ++ ++#define UCB_TC_B 0x06 ++#define UCB_TC_B_VOICE_ENA (1 << 3) ++#define UCB_TC_B_CLIP (1 << 4) ++#define UCB_TC_B_ATT (1 << 6) ++#define UCB_TC_B_SIDE_ENA (1 << 11) ++#define UCB_TC_B_MUTE (1 << 13) ++#define UCB_TC_B_IN_ENA (1 << 14) ++#define UCB_TC_B_OUT_ENA (1 << 15) ++ ++#define UCB_AC_A 0x07 ++#define UCB_AC_B 0x08 ++#define UCB_AC_B_LOOP (1 << 8) ++#define UCB_AC_B_MUTE (1 << 13) ++#define UCB_AC_B_IN_ENA (1 << 14) ++#define UCB_AC_B_OUT_ENA (1 << 15) ++ ++#define UCB_TS_CR 0x09 ++#define UCB_TS_CR_TSMX_POW (1 << 0) ++#define UCB_TS_CR_TSPX_POW (1 << 1) ++#define UCB_TS_CR_TSMY_POW (1 << 2) ++#define UCB_TS_CR_TSPY_POW (1 << 3) ++#define UCB_TS_CR_TSMX_GND (1 << 4) ++#define UCB_TS_CR_TSPX_GND (1 << 5) ++#define UCB_TS_CR_TSMY_GND (1 << 6) ++#define UCB_TS_CR_TSPY_GND (1 << 7) ++#define UCB_TS_CR_MODE_INT (0 << 8) ++#define UCB_TS_CR_MODE_PRES (1 << 8) ++#define UCB_TS_CR_MODE_POS (2 << 8) ++#define UCB_TS_CR_BIAS_ENA (1 << 11) ++#define UCB_TS_CR_TSPX_LOW (1 << 12) ++#define UCB_TS_CR_TSMX_LOW (1 << 13) ++ ++#define UCB_ADC_CR 0x0a ++#define UCB_ADC_SYNC_ENA (1 << 0) ++#define UCB_ADC_VREFBYP_CON (1 << 1) ++#define UCB_ADC_INP_TSPX (0 << 2) ++#define UCB_ADC_INP_TSMX (1 << 2) ++#define UCB_ADC_INP_TSPY (2 << 2) ++#define UCB_ADC_INP_TSMY (3 << 2) ++#define UCB_ADC_INP_AD0 (4 << 2) ++#define UCB_ADC_INP_AD1 (5 << 2) ++#define UCB_ADC_INP_AD2 (6 << 2) ++#define UCB_ADC_INP_AD3 (7 << 2) ++#define UCB_ADC_EXT_REF (1 << 5) ++#define UCB_ADC_START (1 << 7) ++#define UCB_ADC_ENA (1 << 15) ++ ++#define UCB_ADC_DATA 0x0b ++#define UCB_ADC_DAT_VAL (1 << 15) ++#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5) ++ ++#define UCB_ID 0x0c ++#define UCB_ID_1200 0x1004 ++#define UCB_ID_1300 0x1005 ++ ++#define UCB_MODE 0x0d ++#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) ++#define UCB_MODE_AUD_OFF_CAN (1 << 13) ++ ++#include "mcp.h" ++ ++struct ucb1x00; ++ ++struct ucb1x00_irq { ++ void *devid; ++ void (*fn)(int, void *); ++}; ++ ++struct ucb1x00 { ++ spinlock_t lock; ++ struct mcp *mcp; ++ struct pm_dev *pmdev; ++ unsigned int irq; ++ struct semaphore adc_sem; ++ spinlock_t io_lock; ++ u16 id; ++ u16 io_dir; ++ u16 io_out; ++ u16 adc_cr; ++ u16 irq_fal_enbl; ++ u16 irq_ris_enbl; ++ struct ucb1x00_irq irq_handler[16]; ++}; ++ ++/** ++ * ucb1x00_clkrate - return the UCB1x00 SIB clock rate ++ * @ucb: UCB1x00 structure describing chip ++ * ++ * Return the SIB clock rate in Hz. ++ */ ++static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb) ++{ ++ return mcp_get_sclk_rate(ucb->mcp); ++} ++ ++/** ++ * ucb1x00_enable - enable the UCB1x00 SIB clock ++ * @ucb: UCB1x00 structure describing chip ++ * ++ * Enable the SIB clock. This can be called multiple times. ++ */ ++static inline void ucb1x00_enable(struct ucb1x00 *ucb) ++{ ++ mcp_enable(ucb->mcp); ++} ++ ++/** ++ * ucb1x00_disable - disable the UCB1x00 SIB clock ++ * @ucb: UCB1x00 structure describing chip ++ * ++ * Disable the SIB clock. The SIB clock will only be disabled ++ * when the number of ucb1x00_enable calls match the number of ++ * ucb1x00_disable calls. ++ */ ++static inline void ucb1x00_disable(struct ucb1x00 *ucb) ++{ ++ mcp_disable(ucb->mcp); ++} ++ ++/** ++ * ucb1x00_reg_write - write a UCB1x00 register ++ * @ucb: UCB1x00 structure describing chip ++ * @reg: UCB1x00 4-bit register index to write ++ * @val: UCB1x00 16-bit value to write ++ * ++ * Write the UCB1x00 register @reg with value @val. The SIB ++ * clock must be running for this function to return. ++ */ ++static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val) ++{ ++ mcp_reg_write(ucb->mcp, reg, val); ++} ++ ++/** ++ * ucb1x00_reg_read - read a UCB1x00 register ++ * @ucb: UCB1x00 structure describing chip ++ * @reg: UCB1x00 4-bit register index to write ++ * ++ * Read the UCB1x00 register @reg and return its value. The SIB ++ * clock must be running for this function to return. ++ */ ++static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg) ++{ ++ return mcp_reg_read(ucb->mcp, reg); ++} ++/** ++ * ucb1x00_set_audio_divisor - ++ * @ucb: UCB1x00 structure describing chip ++ * @div: SIB clock divisor ++ */ ++static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div) ++{ ++ mcp_set_audio_divisor(ucb->mcp, div); ++} ++ ++/** ++ * ucb1x00_set_telecom_divisor - ++ * @ucb: UCB1x00 structure describing chip ++ * @div: SIB clock divisor ++ */ ++static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div) ++{ ++ mcp_set_telecom_divisor(ucb->mcp, div); ++} ++ ++struct ucb1x00 *ucb1x00_get(void); ++ ++void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int); ++void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int); ++unsigned int ucb1x00_io_read(struct ucb1x00 *ucb); ++ ++#define UCB_NOSYNC (0) ++#define UCB_SYNC (1) ++ ++unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync); ++void ucb1x00_adc_enable(struct ucb1x00 *ucb); ++void ucb1x00_adc_disable(struct ucb1x00 *ucb); ++ ++/* ++ * Which edges of the IRQ do you want to control today? ++ */ ++#define UCB_RISING (1 << 0) ++#define UCB_FALLING (1 << 1) ++ ++int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); ++void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); ++void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); ++int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid); ++ ++#endif +diff -urN linux-2.4.26/drivers/mtd/chips/cfi_probe.c linux-2.4.26-vrs1/drivers/mtd/chips/cfi_probe.c +--- linux-2.4.26/drivers/mtd/chips/cfi_probe.c 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/mtd/chips/cfi_probe.c 2004-01-14 21:32:25.000000000 +0000 +@@ -65,6 +65,10 @@ + return 0; + } + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); ++ ++ /* some devices don't respond to 0xF0, so send 0xFF to be sure */ ++ cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); ++ + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); + + if (!qry_present(map,base,cfi)) +@@ -84,6 +88,8 @@ + /* Eep. This chip also had the QRY marker. + * Is it an alias for the new one? */ + cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL); ++ /* some devices don't respond to 0xF0, so send 0xFF to be sure */ ++ cfi_send_gen_cmd(0xFF, 0, chips[i].start, map, cfi, cfi->device_type, NULL); + + /* If the QRY marker goes away, it's an alias */ + if (!qry_present(map, chips[i].start, cfi)) { +@@ -96,7 +102,8 @@ + * too and if it's the same, assume it's an alias. */ + /* FIXME: Use other modes to do a proper check */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); +- ++ /* some devices don't respond to 0xF0, so send 0xFF to be sure */ ++ cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + if (qry_present(map, base, cfi)) { + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", + map->name, base, chips[i].start); +@@ -119,6 +126,10 @@ + /* Put it back into Read Mode */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + ++ /* some devices don't respond to 0xF0, so send 0xFF to be sure */ ++ cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); ++ ++ + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n", + map->name, cfi->interleave, cfi->device_type*8, base, + map->buswidth*8); +@@ -165,6 +176,20 @@ + cfi->cfiq->InterfaceDesc = le16_to_cpu(cfi->cfiq->InterfaceDesc); + cfi->cfiq->MaxBufWriteSize = le16_to_cpu(cfi->cfiq->MaxBufWriteSize); + ++ /* ++ * ST screwed up the CFI interface for buffer writes on their parts, ++ * so this needs to be fixed up by hand here. ++ * ++ * A possible enhancment is that instead of just reverting back ++ * to word write (as this does), we could use the ST specific double ++ * word write instead. ++ */ ++ ++ if (cfi_read_query(map,base) == 0x20){ ++ cfi->cfiq->BufWriteTimeoutTyp = 0; ++ cfi->cfiq->BufWriteTimeoutMax = 0; ++ } ++ + #ifdef DEBUG_CFI + /* Dump the information therein */ + print_cfi_ident(cfi->cfiq); +@@ -182,6 +207,9 @@ + /* Put it back into Read Mode */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + ++ /* some devices don't respond to 0xF0, so send 0xFF to be sure */ ++ cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); ++ + return 1; + } + +diff -urN linux-2.4.26/drivers/mtd/chips/jedec_probe.c linux-2.4.26-vrs1/drivers/mtd/chips/jedec_probe.c +--- linux-2.4.26/drivers/mtd/chips/jedec_probe.c 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/mtd/chips/jedec_probe.c 2004-04-18 21:33:04.000000000 +0100 +@@ -100,6 +100,8 @@ + #define M29W040B 0x00E3 + + /* SST */ ++#define SST29EE020 0x0010 ++#define SST29LE020 0x0012 + #define SST29EE512 0x005d + #define SST29LE512 0x003d + #define SST39LF800 0x2781 +@@ -839,6 +841,24 @@ + } + }, { + mfr_id: MANUFACTURER_SST, ++ dev_id: SST29EE020, ++ name: "SST 29EE020", ++ DevSize: SIZE_256KiB, ++ CmdSet: P_ID_SST_PAGE, ++ NumEraseRegions: 1, ++ regions: {ERASEINFO(0x01000,64), ++ } ++ }, { ++ mfr_id: MANUFACTURER_SST, ++ dev_id: SST29LE020, ++ name: "SST 29LE020", ++ DevSize: SIZE_256KiB, ++ CmdSet: P_ID_SST_PAGE, ++ NumEraseRegions: 1, ++ regions: {ERASEINFO(0x01000,64), ++ } ++ }, { ++ mfr_id: MANUFACTURER_SST, + dev_id: SST39LF020, + name: "SST 39LF020", + DevSize: SIZE_256KiB, +@@ -937,7 +957,20 @@ + struct cfi_private *cfi) + { + /* Reset */ +- cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); ++ ++ /* after checking the datasheets for SST, MACRONIX and ATMEL ++ * (oh and incidentaly the jedec spec - 3.5.3.3) the reset ++ * sequence is *supposed* to be 0xaa at 0x5555, 0x55 at ++ * 0x2aaa, 0xF0 at 0x5555 this will not affect the AMD chips ++ * as they will ignore the writes and dont care what address ++ * the F0 is written to */ ++ if(cfi->addr_unlock1) { ++ /*printk("reset unlock called %x %x \n",cfi->addr_unlock1,cfi->addr_unlock2);*/ ++ cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ } ++ ++ cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + /* Some misdesigned intel chips do not respond for 0xF0 for a reset, + * so ensure we're in read mode. Send both the Intel and the AMD command + * for this. Intel uses 0xff for this, AMD uses 0xff for NOP, so +diff -urN linux-2.4.26/drivers/mtd/devices/Config.in linux-2.4.26-vrs1/drivers/mtd/devices/Config.in +--- linux-2.4.26/drivers/mtd/devices/Config.in 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/mtd/devices/Config.in 2004-01-14 21:32:25.000000000 +0000 +@@ -17,6 +17,15 @@ + if [ "$CONFIG_SA1100_LART" = "y" ]; then + dep_tristate ' 28F160xx flash driver for LART' CONFIG_MTD_LART $CONFIG_MTD + fi ++if [ "$CONFIG_ARCH_MX1ADS" = "y" ]; then ++ dep_tristate ' SyncFlash driver for MX1ADS' CONFIG_MTD_SYNCFLASH $CONFIG_MTD ++fi ++if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then ++ dep_tristate ' AT91RM9200 DataFlash support' CONFIG_MTD_AT91_DATAFLASH $CONFIG_MTD ++ if [ "$CONFIG_MTD_AT91_DATAFLASH" = "y" -o "$CONFIG_MTD_AT91_DATAFLASH" = "m" ]; then ++ bool ' Enable DataFlash card? ' CONFIG_MTD_AT91_DATAFLASH_CARD ++ fi ++fi + dep_tristate ' Test driver using RAM' CONFIG_MTD_MTDRAM $CONFIG_MTD + if [ "$CONFIG_MTD_MTDRAM" = "y" -o "$CONFIG_MTD_MTDRAM" = "m" ]; then + int 'MTDRAM device size in KiB' CONFIG_MTDRAM_TOTAL_SIZE 4096 +diff -urN linux-2.4.26/drivers/mtd/devices/Makefile linux-2.4.26-vrs1/drivers/mtd/devices/Makefile +--- linux-2.4.26/drivers/mtd/devices/Makefile 2002-11-28 23:53:13.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/mtd/devices/Makefile 2004-01-14 21:32:25.000000000 +0000 +@@ -21,6 +21,7 @@ + obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o + obj-$(CONFIG_MTD_MTDRAM) += mtdram.o + obj-$(CONFIG_MTD_LART) += lart.o ++obj-$(CONFIG_MTD_SYNCFLASH) += syncflash.o + obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o + + include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/mtd/devices/syncflash.c linux-2.4.26-vrs1/drivers/mtd/devices/syncflash.c +--- linux-2.4.26/drivers/mtd/devices/syncflash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/mtd/devices/syncflash.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,615 @@ ++/* ++ * MTD driver for Micron SyncFlash flash memory. ++ * ++ * Author: Jon McClintock ++ * ++ * Based loosely upon the LART flash driver, authored by Abraham vd Merwe ++ * . ++ * ++ * Copyright 2003, Blue Mug, Inc. for Motorola, Inc. ++ * ++ * This code is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * References: ++ * ++ * [1] Micron SyncFlash homepage ++ * - http://www.syncflash.com/ ++ * ++ * [2] MT28S4M16LC -- 4Mx16 SyncFlash memory datasheet ++ * - http://syncflash.com/pdfs/datasheets/mt28s4m16lc_6.pdf ++ * ++ * [3] MTD internal API documentation ++ * - http://www.linux-mtd.infradead.org/tech/ ++ * ++ * Limitations: ++ * ++ * Even though this driver is written for Micron SyncFlash, it is quite ++ * specific to the Motorola MX1 ADS development board. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* partition support */ ++#define HAVE_PARTITIONS ++#ifdef HAVE_PARTITIONS ++#include ++#endif ++ ++#ifndef CONFIG_ARCH_MX1ADS ++#error The SyncFlash driver currently only supports the MX1 ADS platform. ++#endif ++ ++/* ++ * General flash configuration parameters. ++ */ ++#define BUSWIDTH 4 ++#define FLASH_BLOCKSIZE (256 * 1024 * BUSWIDTH) ++#define FLASH_NUMBLOCKS 16 ++ ++#define BUSWIDTH 4 ++#define FLASH_ADDRESS IO_ADDRESS(MX1ADS_FLASH_BASE) ++ ++#define FLASH_MANUFACTURER 0x002C002C ++#define FLASH_DEVICE_ID 0x00D300D3 ++ ++/* ++ * The size and extent of the bootloader in flash. ++ */ ++#define NUM_BOOTLOADER_BLOCKS 1 ++#define BOOTLOADER_START 0x00000000 ++#define BOOTLOADER_LEN (NUM_BOOTLOADER_BLOCKS * FLASH_BLOCKSIZE) ++ ++/* ++ * The size and extent of the kernel in flash. ++ */ ++#define NUM_KERNEL_BLOCKS 1 ++#define KERNEL_START (BOOTLOADER_START + BOOTLOADER_LEN) ++#define KERNEL_LEN (NUM_KERNEL_BLOCKS * FLASH_BLOCKSIZE) ++ ++/* File system */ ++#define NUM_FILESYSTEM_BLOCKS 14 ++#define FILESYSTEM_START (KERNEL_START + KERNEL_LEN) ++#define FILESYSTEM_LEN (NUM_FILESYSTEM_BLOCKS * FLASH_BLOCKSIZE) ++ ++ ++/* ++ * SDRAM controller register location and values. These are very specific ++ * to the MX1. ++ */ ++#define SDRAMC_REGISTER IO_ADDRESS(0x00221004) ++ ++/* ++ * This the mask we use to get the start of a block from a given address. ++ */ ++#define BLOCK_MASK (0xFFF00000) ++ ++/* ++ * This is the A10 address line of the SyncFlash; it's used to initiate ++ * a precharge command. ++ */ ++#define SYNCFLASH_A10 (0x00100000) ++ ++/* ++ * SDRAM controller MODE settings. ++ */ ++#define CMD_NORMAL (0x81020300) /* Normal Mode */ ++#define CMD_PREC (CMD_NORMAL + 0x10000000) /* Precharge command */ ++#define CMD_AUTO (CMD_NORMAL + 0x20000000) /* Auto refresh */ ++#define CMD_LMR (CMD_NORMAL + 0x30000000) /* Load Mode Register */ ++#define CMD_LCR (CMD_NORMAL + 0x60000000) /* LCR Command */ ++#define CMD_PROGRAM (CMD_NORMAL + 0x70000000) /* SyncFlash Program */ ++ ++/* ++ * SyncFlash LCR Commands adjusted for the DBMX1 AHB internal address bus . ++ */ ++#define LCR_READ_STATUS (0x0001C000) /* 0x70 */ ++#define LCR_READ_CONFIG (0x00024000) /* 0x90 */ ++#define LCR_ERASE_CONFIRM (0x00008000) /* 0x20 */ ++#define LCR_ERASE_NVMODE (0x0000C000) /* 0x30 */ ++#define LCR_PROG_NVMODE (0x00028000) /* 0xA0 */ ++#define LCR_SR_CLEAR (0x00014000) /* 0x50 */ ++ ++/* ++ * Status register bits ++ */ ++#define SR_VPS_ERROR (1 << 8) /* Power-Up status error */ ++#define SR_ISM_READY (1 << 7) /* State machine isn't busy */ ++#define SR_ERASE_ERROR (1 << 5) /* Erase/Unprotect error */ ++#define SR_PROGRAM_ERROR (1 << 4) /* Program/Protect error */ ++#define SR_DEVICE_PROTECTED (1 << 3) /* Device is protected */ ++#define SR_ISM_STATUS_H (1 << 2) /* Bank ISM status, high bit */ ++#define SR_ISM_STATUS_L (1 << 1) /* Bank ISM status, low bit */ ++#define SR_DEVICE_ISM_STATUS (1 << 0) /* ISM is device-level */ ++ ++#define SR_ERROR (SR_VPS_ERROR|SR_ERASE_ERROR|SR_PROGRAM_ERROR|SR_DEVICE_PROTECTED) ++ ++#define STATUS_VALUE(a) ((a) | ((a) << 16)) ++ ++/* ++ * Device configuration register offsets ++ */ ++#define DC_MANUFACTURER (0 * BUSWIDTH) ++#define DC_DEVICE_ID (1 * BUSWIDTH) ++#define DC_BLOCK_PROTECT (2 * BUSWIDTH) ++#define DC_DEVICE_PROTECT (3 * BUSWIDTH) ++ ++#define FL_WORD(addr) (*(volatile unsigned long*)(addr)) ++ ++static char module_name[] = "syncflash"; ++ ++inline __u8 read8 (__u32 offset) ++{ ++ return *(volatile __u8 *) (FLASH_ADDRESS + offset); ++} ++ ++inline __u32 read32 (__u32 offset) ++{ ++ return *(volatile __u32 *) (FLASH_ADDRESS + offset); ++} ++ ++inline void write32 (__u32 x,__u32 offset) ++{ ++ *(volatile __u32 *) (FLASH_ADDRESS + offset) = x; ++} ++ ++static __u32 read_device_configuration_register(__u32 reg_number) ++{ ++ __u32 tmp; ++ ++ /* Setup the SDRAM controller to issue an LCR command. */ ++ FL_WORD(SDRAMC_REGISTER) = CMD_LCR; ++ ++ /* Perform a read to issue the Read Device Configuration Command. */ ++ tmp = read32(LCR_READ_CONFIG); ++ ++ /* Return the SDRAM controller to normal mode. */ ++ FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL; ++ ++ /* Return the value of the specified register. */ ++ tmp = read32(reg_number); ++ ++ return tmp; ++} ++ ++/* ++ * Get the status of the flash devices. ++ */ ++static __u32 flash_read_status() ++{ ++ __u32 status, tmp; ++ ++ /* Enter the SyncFlash Program READ/WRITE mode. */ ++ FL_WORD(SDRAMC_REGISTER) = CMD_PROGRAM; ++ ++ /* Read the status register. */ ++ status = read32(LCR_READ_STATUS); ++ ++ /* Clear the status register. */ ++ FL_WORD(SDRAMC_REGISTER) = CMD_LCR; ++ tmp = read32(LCR_SR_CLEAR); ++ ++ /* Return to Normal mode. */ ++ FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL; ++ ++ return status; ++} ++ ++/* ++ * Loop until both write state machines are ready. ++ */ ++static __u32 flash_status_wait() ++{ ++ __u32 status; ++ do { ++ status = flash_read_status(); ++ } while ((status & STATUS_VALUE(SR_ISM_READY)) != ++ STATUS_VALUE(SR_ISM_READY)); ++ return status; ++} ++ ++/* ++ * Loop until the Write State machine is ready, then do a full error ++ * check. Clear status and leave the flash in Read Array mode; return ++ * 0 for no error, -1 for error. ++ */ ++static int flash_status_full_check() ++{ ++ __u32 status; ++ ++ status = flash_status_wait() & STATUS_VALUE(SR_ERROR); ++ return status ? -EIO : 0; ++} ++ ++/* ++ * Return the flash to the normal mode. ++ */ ++static void flash_normal_mode() ++{ ++ __u32 tmp; ++ ++ /* First issue a precharge all command. */ ++ FL_WORD(SDRAMC_REGISTER) = CMD_PREC; ++ tmp = read32(SYNCFLASH_A10); ++ ++ /* Now place the SDRAM controller in Normal mode. */ ++ FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL; ++} ++ ++/* ++ * Probe for SyncFlash memory on MX1ADS board. ++ * ++ * Returns 1 if we found SyncFlash memory, 0 otherwise. ++ */ ++static int flash_probe (void) ++{ ++ __u32 manufacturer, device_id; ++ ++ /* For some reason, the first read doesn't work, so we do it ++ * twice. */ ++ manufacturer = read_device_configuration_register(DC_MANUFACTURER); ++ manufacturer = read_device_configuration_register(DC_MANUFACTURER); ++ device_id = read_device_configuration_register(DC_DEVICE_ID); ++ ++ printk("SyncFlash probe: manufacturer 0x%08lx, device_id 0x%08lx\n", ++ manufacturer, device_id); ++ return (manufacturer == FLASH_MANUFACTURER && ++ device_id == FLASH_DEVICE_ID); ++} ++ ++/* ++ * Erase one block of flash memory at offset ``offset'' which is any ++ * address within the block which should be erased. ++ * ++ * Returns 0 if successful, -1 otherwise. ++ */ ++static inline int erase_block (__u32 offset) ++{ ++ __u32 tmp; ++ ++ /* Mask off the lower bits of the address to get the first address ++ * in the flash block. */ ++ offset &= (__u32)BLOCK_MASK; ++ ++ /* Perform a read and precharge of the bank before the LCR|ACT|WRIT ++ * sequence to avoid the inadvertent precharge command occurring ++ * during the LCR_ACT_WRIT sequence. */ ++ FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL; ++ tmp = read32(offset); ++ FL_WORD(SDRAMC_REGISTER) = CMD_PREC; ++ tmp = read32(offset); ++ ++ /* Now start the actual erase. */ ++ ++ /* LCR|ACT|WRIT sequence */ ++ FL_WORD(SDRAMC_REGISTER) = CMD_LCR; ++ write32(0, offset + LCR_ERASE_CONFIRM); ++ ++ /* Return to normal mode to issue the erase confirm. */ ++ FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL; ++ write32(0xD0D0D0D0, offset); ++ ++ if (flash_status_full_check()) { ++ printk (KERN_WARNING "%s: erase error at address 0x%.8x.\n", ++ module_name, offset); ++ return (-1); ++ } ++ ++ flash_normal_mode(); ++ ++ return 0; ++} ++ ++static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) ++{ ++ __u32 addr,len; ++ int i,first; ++ ++ /* sanity checks */ ++ if (instr->addr + instr->len > mtd->size) return (-EINVAL); ++ ++ /* ++ * check that both start and end of the requested erase are ++ * aligned with the erasesize at the appropriate addresses. ++ * ++ * skip all erase regions which are ended before the start of ++ * the requested erase. Actually, to save on the calculations, ++ * we skip to the first erase region which starts after the ++ * start of the requested erase, and then go back one. ++ */ ++ for (i = 0; (i < mtd->numeraseregions) && ++ (instr->addr >= mtd->eraseregions[i].offset); i++) ; ++ i--; ++ ++ /* ++ * ok, now i is pointing at the erase region in which this ++ * erase request starts. Check the start of the requested ++ * erase range is aligned with the erase size which is in ++ * effect here. ++ */ ++ if (instr->addr & (mtd->eraseregions[i].erasesize - 1)) ++ return (-EINVAL); ++ ++ /* Remember the erase region we start on */ ++ first = i; ++ ++ /* ++ * next, check that the end of the requested erase is aligned ++ * with the erase region at that address. ++ * ++ * as before, drop back one to point at the region in which ++ * the address actually falls ++ */ ++ for (; ++ (i < mtd->numeraseregions) && ++ ((instr->addr + instr->len) >= mtd->eraseregions[i].offset) ; ++ i++) ; ++ i--; ++ ++ /* is the end aligned on a block boundary? */ ++ if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)) ++ return (-EINVAL); ++ ++ addr = instr->addr; ++ len = instr->len; ++ ++ i = first; ++ ++ /* now erase those blocks */ ++ while (len) ++ { ++ if (erase_block (addr)) ++ { ++ instr->state = MTD_ERASE_FAILED; ++ return (-EIO); ++ } ++ ++ addr += mtd->eraseregions[i].erasesize; ++ len -= mtd->eraseregions[i].erasesize; ++ ++ if (addr == (mtd->eraseregions[i].offset + ++ (mtd->eraseregions[i].erasesize * ++ mtd->eraseregions[i].numblocks))) ++ i++; ++ } ++ ++ instr->state = MTD_ERASE_DONE; ++ if (instr->callback) instr->callback (instr); ++ ++ return (0); ++} ++ ++static int flash_read (struct mtd_info *mtd, loff_t from, ++ size_t len, size_t *retlen, u_char *buf) ++{ ++ /* Sanity checks. */ ++ if (!len) return (0); ++ if (from + len > mtd->size) return (-EINVAL); ++ ++ /* Ensure that we are in normal mode. */ ++ flash_normal_mode(); ++ ++ /* We always read len bytes. */ ++ *retlen = len; ++ ++ /* first, we read bytes until we reach a dword boundary */ ++ if (from & (BUSWIDTH - 1)) ++ { ++ int gap = BUSWIDTH - (from & (BUSWIDTH - 1)); ++ while (len && gap--) *buf++ = read8(from++), len--; ++ } ++ ++ /* now we read dwords until we reach a non-dword boundary */ ++ while (len >= BUSWIDTH) ++ { ++ *((__u32 *) buf) = read32(from); ++ ++ buf += BUSWIDTH; ++ from += BUSWIDTH; ++ len -= BUSWIDTH; ++ } ++ ++ /* top up the last unaligned bytes */ ++ if (len & (BUSWIDTH - 1)) ++ while (len--) *buf++ = read8(from++); ++ ++ return (0); ++} ++ ++/* ++ * Write one dword ``x'' to flash memory at offset ``offset''. ``offset'' ++ * must be 32 bits, i.e. it must be on a dword boundary. ++ * ++ * Returns 0 if successful, -1 otherwise. ++ */ ++static int flash_write_dword(__u32 offset, __u32 x) ++{ ++ __u32 tmp; ++ ++ /* First issue a precharge all command. */ ++ FL_WORD(SDRAMC_REGISTER) = CMD_PREC; ++ tmp = read32(SYNCFLASH_A10); ++ ++ /* Enter the SyncFlash programming mode. */ ++ FL_WORD(SDRAMC_REGISTER) = CMD_PROGRAM; ++ write32(x, offset); ++ ++ /* Wait for the write to complete. */ ++ flash_status_wait(); ++ ++ /* Return to normal mode. */ ++ flash_normal_mode(); ++ ++ return 0; ++} ++ ++static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen,const u_char *buf) ++{ ++ __u8 tmp[4]; ++ int i,n; ++ ++ *retlen = 0; ++ ++ /* Sanity checks */ ++ if (!len) return (0); ++ if (to + len > mtd->size) return (-EINVAL); ++ ++ /* First, we write a 0xFF.... padded byte until we reach a ++ * dword boundary. */ ++ if (to & (BUSWIDTH - 1)) ++ { ++ __u32 aligned = to & ~(BUSWIDTH - 1); ++ int gap = to - aligned; ++ ++ i = n = 0; ++ ++ while (gap--) tmp[i++] = 0xFF; ++ while (len && i < BUSWIDTH) tmp[i++] = buf[n++], len--; ++ while (i < BUSWIDTH) tmp[i++] = 0xFF; ++ ++ if (flash_write_dword(aligned, *((__u32 *) tmp))) ++ return (-EIO); ++ ++ to += n; ++ buf += n; ++ *retlen += n; ++ } ++ ++ /* Now we write dwords until we reach a non-dword boundary. */ ++ while (len >= BUSWIDTH) ++ { ++ if (flash_write_dword (to,*((__u32 *) buf))) return (-EIO); ++ ++ to += BUSWIDTH; ++ buf += BUSWIDTH; ++ *retlen += BUSWIDTH; ++ len -= BUSWIDTH; ++ } ++ ++ /* Top up the last unaligned bytes, padded with 0xFF.... */ ++ if (len & (BUSWIDTH - 1)) ++ { ++ i = n = 0; ++ ++ while (len--) tmp[i++] = buf[n++]; ++ while (i < BUSWIDTH) tmp[i++] = 0xFF; ++ ++ if (flash_write_dword (to,*((__u32 *) tmp))) return (-EIO); ++ ++ *retlen += n; ++ } ++ ++ return flash_status_full_check(); ++} ++ ++ ++ ++#define NB_OF(x) (sizeof (x) / sizeof (x[0])) ++ ++static struct mtd_info mtd; ++ ++static struct mtd_erase_region_info erase_regions[] = ++{ ++ /* flash blocks */ ++ { ++ offset: 0x00000000, ++ erasesize: FLASH_BLOCKSIZE, ++ numblocks: FLASH_NUMBLOCKS ++ }, ++}; ++ ++#ifdef HAVE_PARTITIONS ++static struct mtd_partition syncflash_partitions[] = ++{ ++ /* bootloader */ ++ { ++ name: "bootloader", ++ offset: BOOTLOADER_START, ++ size: BOOTLOADER_LEN, ++ mask_flags: 0 ++ }, ++ /* Kernel */ ++ { ++ name: "kernel", ++ offset: KERNEL_START, /* MTDPART_OFS_APPEND */ ++ size: KERNEL_LEN, ++ mask_flags: 0 ++ }, ++ /* file system */ ++ { ++ name: "file system", ++ offset: FILESYSTEM_START, /* MTDPART_OFS_APPEND */ ++ size: FILESYSTEM_LEN, /* MTDPART_SIZ_FULL */ ++ mask_flags: 0 ++ } ++}; ++#endif ++ ++int __init syncflash_init (void) ++{ ++ int result; ++ ++ memset (&mtd,0,sizeof (mtd)); ++ ++ printk ("MTD driver for Micron SyncFlash.\n"); ++ printk ("%s: Probing for SyncFlash on MX1ADS...\n",module_name); ++ ++ if (!flash_probe ()) ++ { ++ printk (KERN_WARNING "%s: Found no SyncFlash devices\n", ++ module_name); ++ return (-ENXIO); ++ } ++ ++ printk ("%s: Found a SyncFlash device.\n",module_name); ++ ++ mtd.name = module_name; ++ mtd.type = MTD_NORFLASH; ++ mtd.flags = MTD_CAP_NORFLASH; ++ mtd.size = FLASH_BLOCKSIZE * FLASH_NUMBLOCKS; ++ ++ mtd.erasesize = FLASH_BLOCKSIZE; ++ mtd.numeraseregions = NB_OF(erase_regions); ++ mtd.eraseregions = erase_regions; ++ ++ mtd.module = THIS_MODULE; ++ ++ mtd.erase = flash_erase; ++ mtd.read = flash_read; ++ mtd.write = flash_write; ++ ++#ifndef HAVE_PARTITIONS ++ result = add_mtd_device(&mtd); ++#else ++ result = add_mtd_partitions(&mtd, ++ syncflash_partitions, ++ NB_OF(syncflash_partitions)); ++#endif ++ ++ return (result); ++} ++ ++void __exit syncflash_exit (void) ++{ ++#ifndef HAVE_PARTITIONS ++ del_mtd_device (&mtd); ++#else ++ del_mtd_partitions (&mtd); ++#endif ++} ++ ++module_init (syncflash_init); ++module_exit (syncflash_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jon McClintock "); ++MODULE_DESCRIPTION("MTD driver for Micron MT28S4M16LC SyncFlash on MX1ADS board"); ++ ++ +diff -urN linux-2.4.26/drivers/mtd/maps/Config.in linux-2.4.26-vrs1/drivers/mtd/maps/Config.in +--- linux-2.4.26/drivers/mtd/maps/Config.in 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/mtd/maps/Config.in 2004-01-14 21:32:26.000000000 +0000 +@@ -81,10 +81,10 @@ + dep_tristate ' CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS + dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE + dep_tristate ' CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_ARCH_IQ80310 +- dep_tristate ' CFI Flash device mapped on Epxa10db' CONFIG_MTD_EPXA10DB $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT +- dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_SA1100_FORTUNET ++ dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_FORTUNET ++ dep_tristate ' CFI Flash device mapped on Epxa' CONFIG_MTD_EPXA $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT + dep_tristate ' NV-RAM mapping AUTCPU12 board' CONFIG_MTD_AUTCPU12 $CONFIG_ARCH_AUTCPU12 +- dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_MTD_CFI ++ dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_ARCH_EDB7212 $CONFIG_MTD_CFI + dep_tristate ' JEDEC Flash device mapped on impA7' CONFIG_MTD_IMPA7 $CONFIG_MTD_JEDECPROBE + dep_tristate ' JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame' CONFIG_MTD_CEIVA $CONFIG_MTD_JEDECPROBE $CONFIG_ARCH_CEIVA + fi +diff -urN linux-2.4.26/drivers/mtd/maps/Makefile linux-2.4.26-vrs1/drivers/mtd/maps/Makefile +--- linux-2.4.26/drivers/mtd/maps/Makefile 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/mtd/maps/Makefile 2004-01-14 21:32:26.000000000 +0000 +@@ -3,11 +3,7 @@ + # + # $Id: Makefile,v 1.37 2003/01/24 14:26:38 dwmw2 Exp $ + +-BELOW25 := $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/) +- +-ifeq ($(BELOW25),y) + O_TARGET := mapslink.o +-endif + + # Chip mappings + obj-$(CONFIG_MTD_CDB89712) += cdb89712.o +@@ -17,7 +13,7 @@ + obj-$(CONFIG_MTD_DC21285) += dc21285.o + obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o + obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o +-obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o ++obj-$(CONFIG_MTD_EPXA) += epxa-flash.o + obj-$(CONFIG_MTD_IQ80310) += iq80310.o + obj-$(CONFIG_MTD_L440GX) += l440gx.o + obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o +@@ -29,9 +25,9 @@ + obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o + ifneq ($(CONFIG_MTD_PHYSMAP),n) + ifeq ($(CONFIG_MTD_PHYSMAP_BUSWIDTH),8) +- obj-$(CONFIG_MTD_PHYSMAP) += physmap64.o ++ obj-$(CONFIG_MTD_PHYSMAP) += physmap64.o + else +- obj-$(CONFIG_MTD_PHYSMAP) += physmap.o ++ obj-$(CONFIG_MTD_PHYSMAP) += physmap.o + endif + endif + obj-$(CONFIG_MTD_PNC2000) += pnc2000.o +@@ -39,6 +35,9 @@ + obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o + obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o + obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o ++ifeq ($(CONFIG_ASSABET_NEPONSET),y) ++ obj-$(CONFIG_MTD_SA1100) += neponset-flash.o ++endif + obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o + obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o + obj-$(CONFIG_MTD_NETSC520) += netsc520.o +diff -urN linux-2.4.26/drivers/mtd/maps/dc21285.c linux-2.4.26-vrs1/drivers/mtd/maps/dc21285.c +--- linux-2.4.26/drivers/mtd/maps/dc21285.c 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/mtd/maps/dc21285.c 2004-04-18 21:33:22.000000000 +0100 +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -18,9 +19,9 @@ + + #include + #include ++#include + +- +-static struct mtd_info *mymtd; ++static struct mtd_info *dc21285_mtd; + + __u8 dc21285_read8(struct map_info *map, unsigned long ofs) + { +@@ -44,6 +45,9 @@ + + void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr) + { ++ if(machine_is_netwinder()) { ++ nw_en_write(); ++ } + *CSR_ROMWRITEREG = adr & 3; + adr &= ~3; + *(__u8*)(map->map_priv_1 + adr) = d; +@@ -51,6 +55,9 @@ + + void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr) + { ++ if(machine_is_netwinder()) { ++ nw_en_write(); ++ } + *CSR_ROMWRITEREG = adr & 3; + adr &= ~3; + *(__u16*)(map->map_priv_1 + adr) = d; +@@ -58,6 +65,9 @@ + + void dc21285_write32(struct map_info *map, __u32 d, unsigned long adr) + { ++ if(machine_is_netwinder()) { ++ nw_en_write(); ++ } + *(__u32*)(map->map_priv_1 + adr) = d; + } + +@@ -105,6 +115,27 @@ + }; + + ++static void ++nw_en_write(void) { ++#ifdef CONFIG_ARCH_NETWINDER ++ extern spinlock_t gpio_lock; ++ unsigned long flags; ++ ++ /* ++ * we want to write a bit pattern XXX1 to Xilinx to enable ++ * the write gate, which will be open for about the next 2ms. ++ */ ++ spin_lock_irqsave(&gpio_lock, flags); ++ cpld_modify(1, 1); ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ ++ /* ++ * let the ISA bus to catch on... ++ */ ++ udelay(25); ++#endif ++} ++ + /* Partition stuff */ + static struct mtd_partition *dc21285_parts; + +@@ -112,6 +143,9 @@ + + int __init init_dc21285(void) + { ++ int nr_parts = 0; ++ char *part_type = "none"; ++ + /* Determine buswidth */ + switch (*CSR_SA110_CNTL & (3<<14)) { + case SA110_CNTL_ROMWIDTH_8: +@@ -137,24 +171,64 @@ + return -EIO; + } + +- mymtd = do_map_probe("cfi_probe", &dc21285_map); +- if (mymtd) { +- int nrparts = 0; ++ if(machine_is_ebsa285()) { ++ dc21285_mtd = do_map_probe("cfi_probe", &dc21285_map); ++ } else { ++ dc21285_mtd = do_map_probe("jedec_probe", &dc21285_map); ++ } + +- mymtd->module = THIS_MODULE; ++ if (!dc21285_mtd) { ++ /* no recognised device so unmap and exit */ ++ iounmap((void *)dc21285_map.map_priv_1); ++ return -ENXIO; ++ } + +- /* partition fixup */ ++ dc21285_mtd->module = THIS_MODULE; + ++ /* ++ * Dynamic partition selection stuff (might override the static ones) ++ */ + #ifdef CONFIG_MTD_REDBOOT_PARTS +- nrparts = parse_redboot_partitions(mymtd, &dc21285_parts); ++ if (nr_parts == 0) { ++ int ret = parse_redboot_partitions(dc21285_mtd, &dc21285_parts); ++ ++ if (ret > 0) { ++ part_type = "RedBoot"; ++ nr_parts = ret; ++ } ++ else ++ { ++ dc21285_parts=NULL; /* ensure partition table remains clear */ ++ } ++ } + #endif +- if (nrparts > 0) { +- add_mtd_partitions(mymtd, dc21285_parts, nrparts); +- } else if (nrparts == 0) { +- printk(KERN_NOTICE "RedBoot partition table failed\n"); +- add_mtd_device(mymtd); ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ if (nr_parts == 0) { ++ int ret = parse_cmdline_partitions(dc21285_mtd, &dc21285_parts, "sa1100"); ++ if (ret > 0) { ++ part_type = "Command Line"; ++ nr_parts = ret; + } ++ else ++ { ++ dc21285_parts=NULL; /* ensure partition table remains clear */ ++ } ++ } ++#endif + ++ if (nr_parts == 0) { ++ printk(KERN_NOTICE "DC21285 Flash: no partition info available, registering whole flash at once\n"); ++ add_mtd_device(dc21285_mtd); ++ } ++#ifdef CONFIG_MTD_PARTITIONS ++ else ++ { ++ printk(KERN_NOTICE "DC21285 Flash: Using %s partition definition\n", part_type); ++ add_mtd_partitions(dc21285_mtd, &dc21285_parts, nr_parts); ++ } ++#endif ++ ++ if(machine_is_ebsa285()) { + /* + * Flash timing is determined with bits 19-16 of the + * CSR_SA110_CNTL. The value is the number of wait cycles, or +@@ -167,20 +241,15 @@ + *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20)); + /* tristate time */ + *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24)); +- +- return 0; + } +- +- iounmap((void *)dc21285_map.map_priv_1); +- return -ENXIO; + } + + static void __exit cleanup_dc21285(void) + { +- if (mymtd) { +- del_mtd_device(mymtd); +- map_destroy(mymtd); +- mymtd = NULL; ++ if (dc21285_mtd) { ++ del_mtd_device(dc21285_mtd); ++ map_destroy(dc21285_mtd); ++ dc21285_mtd = NULL; + } + if (dc21285_map.map_priv_1) { + iounmap((void *)dc21285_map.map_priv_1); +diff -urN linux-2.4.26/drivers/mtd/maps/epxa-flash.c linux-2.4.26-vrs1/drivers/mtd/maps/epxa-flash.c +--- linux-2.4.26/drivers/mtd/maps/epxa-flash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/mtd/maps/epxa-flash.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,234 @@ ++/* ++ * Flash memory access on EPXA based devices ++ * ++ * (C) 2000 Nicolas Pitre ++ * Copyright (C) 2001 Altera Corporation ++ * Copyright (C) 2001 Red Hat, Inc. ++ * ++ * $Id: epxa10db-flash.c,v 1.4 2002/08/22 10:46:19 cdavies Exp $ ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#ifdef CONFIG_EPXA10DB ++#define BOARD_NAME "EPXA10DB" ++#else ++#define BOARD_NAME "EPXA1DB" ++#endif ++ ++static int nr_parts = 0; ++static struct mtd_partition *parts; ++ ++static struct mtd_info *mymtd; ++ ++extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **); ++static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts); ++ ++static __u8 epxa_read8(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readb(map->map_priv_1 + ofs); ++} ++ ++static __u16 epxa_read16(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readw(map->map_priv_1 + ofs); ++} ++ ++static __u32 epxa_read32(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readl(map->map_priv_1 + ofs); ++} ++ ++static void epxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++{ ++ memcpy_fromio(to, map->map_priv_1 + from, len); ++} ++ ++static void epxa_write8(struct map_info *map, __u8 d, unsigned long adr) ++{ ++ __raw_writeb(d, map->map_priv_1 + adr); ++ mb(); ++} ++ ++static void epxa_write16(struct map_info *map, __u16 d, unsigned long adr) ++{ ++ __raw_writew(d, map->map_priv_1 + adr); ++ mb(); ++} ++ ++static void epxa_write32(struct map_info *map, __u32 d, unsigned long adr) ++{ ++ __raw_writel(d, map->map_priv_1 + adr); ++ mb(); ++} ++ ++static void epxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++{ ++ memcpy_toio(map->map_priv_1 + to, from, len); ++} ++ ++static struct map_info epxa_map = { ++ .name = "EPXA flash", ++ .size = FLASH_SIZE, ++ .buswidth = 2, ++ .read8 = epxa_read8, ++ .read16 = epxa_read16, ++ .read32 = epxa_read32, ++ .copy_from = epxa_copy_from, ++ .write8 = epxa_write8, ++ .write16 = epxa_write16, ++ .write32 = epxa_write32, ++ .copy_to = epxa_copy_to ++}; ++ ++static int __init epxa_mtd_init(void) ++{ ++ int i; ++ ++ printk(KERN_NOTICE "%s flash device: %x at %x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); ++ epxa_map.map_priv_1 = (unsigned long)ioremap_nocache(FLASH_START, FLASH_SIZE); ++ if (!epxa_map.map_priv_1) { ++ printk("Failed to ioremap %s flash\n",BOARD_NAME); ++ return -EIO; ++ } ++ ++ mymtd = do_map_probe("cfi_probe", &epxa_map); ++ if (!mymtd) { ++ iounmap((void *)epxa_map.map_priv_1); ++ return -ENXIO; ++ } ++ ++ mymtd->module = THIS_MODULE; ++ ++ /* Unlock the flash device. */ ++ if(mymtd->unlock){ ++ for (i=0; inumeraseregions;i++){ ++ int j; ++ for(j=0;jeraseregions[i].numblocks;j++){ ++ mymtd->unlock(mymtd,mymtd->eraseregions[i].offset + j * mymtd->eraseregions[i].erasesize,mymtd->eraseregions[i].erasesize); ++ } ++ } ++ } ++ ++#ifdef CONFIG_MTD_REDBOOT_PARTS ++ nr_parts = parse_redboot_partitions(mymtd, &parts); ++ ++ if (nr_parts > 0) { ++ add_mtd_partitions(mymtd, parts, nr_parts); ++ return 0; ++ } ++#endif ++#ifdef CONFIG_MTD_AFS_PARTS ++ nr_parts = parse_afs_partitions(mymtd, &parts); ++ ++ if (nr_parts > 0) { ++ add_mtd_partitions(mymtd, parts, nr_parts); ++ return 0; ++ } ++#endif ++ ++ /* No recognised partitioning schemes found - use defaults */ ++ nr_parts = epxa_default_partitions(mymtd, &parts); ++ if (nr_parts > 0) { ++ add_mtd_partitions(mymtd, parts, nr_parts); ++ return 0; ++ } ++ ++ /* If all else fails... */ ++ add_mtd_device(mymtd); ++ return 0; ++} ++ ++static void __exit epxa_mtd_cleanup(void) ++{ ++ if (mymtd) { ++ if (nr_parts) ++ del_mtd_partitions(mymtd); ++ else ++ del_mtd_device(mymtd); ++ map_destroy(mymtd); ++ } ++ if (epxa_map.map_priv_1) { ++ iounmap((void *)epxa_map.map_priv_1); ++ epxa_map.map_priv_1 = 0; ++ } ++} ++ ++ ++/* ++ * This will do for now, once we decide which bootldr we're finally ++ * going to use then we'll remove this function and do it properly ++ * ++ * Partions are currently (as offsets from base of flash): ++ * 0x00000000 - 0x003FFFFF - bootloader (!) ++ * 0x00400000 - 0x00FFFFFF - Flashdisk ++ */ ++ ++static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts) ++{ ++ struct mtd_partition *parts; ++ int ret; ++ int npartitions = 0; ++ char *names; ++ const char *name = "jffs"; ++ ++ printk("Using default partitions for %s\n",BOARD_NAME); ++ npartitions=1; ++ parts = kmalloc(npartitions*sizeof(*parts)+strlen(name)+1, GFP_KERNEL); ++ if (!parts) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ memzero(parts,npartitions*sizeof(*parts)+strlen(name)); ++ ++ names = (char *)&parts[npartitions]; ++ parts[0].name = names; ++ names += strlen(name) + 1; ++ strcpy(parts[0].name, name); ++ ++#ifdef CONFIG_EPXA10DB_R2 ++ parts[0].size = FLASH_SIZE-0x00400000; ++ parts[0].offset = 0x00400000; ++#elif defined CONFIG_EPXA10DB_R3 ++ parts[0].size = 0x00800000; ++ parts[0].offset = 0x00800000; ++#else ++ parts[0].size = FLASH_SIZE-0x00180000; ++ parts[0].offset = 0x00180000; ++#endif ++ ret = npartitions; ++ ++ out: ++ *pparts = parts; ++ return ret; ++} ++ ++ ++module_init(epxa_mtd_init); ++module_exit(epxa_mtd_cleanup); ++ ++MODULE_AUTHOR("Clive Davies"); ++MODULE_DESCRIPTION("Altera epxa mtd flash map"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/mtd/maps/epxa10db-flash.c linux-2.4.26-vrs1/drivers/mtd/maps/epxa10db-flash.c +--- linux-2.4.26/drivers/mtd/maps/epxa10db-flash.c 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/mtd/maps/epxa10db-flash.c 1970-01-01 01:00:00.000000000 +0100 +@@ -1,233 +0,0 @@ +-/* +- * Flash memory access on EPXA based devices +- * +- * (C) 2000 Nicolas Pitre +- * Copyright (C) 2001 Altera Corporation +- * Copyright (C) 2001 Red Hat, Inc. +- * +- * $Id: epxa10db-flash.c,v 1.4 2002/08/22 10:46:19 cdavies Exp $ +- * +- * 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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#ifdef CONFIG_EPXA10DB +-#define BOARD_NAME "EPXA10DB" +-#else +-#define BOARD_NAME "EPXA1DB" +-#endif +- +-static int nr_parts = 0; +-static struct mtd_partition *parts; +- +-static struct mtd_info *mymtd; +- +-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **); +-static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts); +- +-static __u8 epxa_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 epxa_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static __u32 epxa_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-static void epxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void epxa_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void epxa_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void epxa_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void epxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- +- +- +-static struct map_info epxa_map = { +- name: "EPXA flash", +- size: FLASH_SIZE, +- buswidth: 2, +- read8: epxa_read8, +- read16: epxa_read16, +- read32: epxa_read32, +- copy_from: epxa_copy_from, +- write8: epxa_write8, +- write16: epxa_write16, +- write32: epxa_write32, +- copy_to: epxa_copy_to +-}; +- +- +-static int __init epxa_mtd_init(void) +-{ +- int i; +- +- printk(KERN_NOTICE "%s flash device: %x at %x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); +- epxa_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); +- if (!epxa_map.map_priv_1) { +- printk("Failed to ioremap %s flash\n",BOARD_NAME); +- return -EIO; +- } +- +- mymtd = do_map_probe("cfi_probe", &epxa_map); +- if (!mymtd) { +- iounmap((void *)epxa_map.map_priv_1); +- return -ENXIO; +- } +- +- mymtd->module = THIS_MODULE; +- +- /* Unlock the flash device. */ +- if(mymtd->unlock){ +- for (i=0; inumeraseregions;i++){ +- int j; +- for(j=0;jeraseregions[i].numblocks;j++){ +- mymtd->unlock(mymtd,mymtd->eraseregions[i].offset + j * mymtd->eraseregions[i].erasesize,mymtd->eraseregions[i].erasesize); +- } +- } +- } +- +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- nr_parts = parse_redboot_partitions(mymtd, &parts); +- +- if (nr_parts > 0) { +- add_mtd_partitions(mymtd, parts, nr_parts); +- return 0; +- } +-#endif +-#ifdef CONFIG_MTD_AFS_PARTS +- nr_parts = parse_afs_partitions(mymtd, &parts); +- +- if (nr_parts > 0) { +- add_mtd_partitions(mymtd, parts, nr_parts); +- return 0; +- } +-#endif +- +- /* No recognised partitioning schemes found - use defaults */ +- nr_parts = epxa_default_partitions(mymtd, &parts); +- if (nr_parts > 0) { +- add_mtd_partitions(mymtd, parts, nr_parts); +- return 0; +- } +- +- /* If all else fails... */ +- add_mtd_device(mymtd); +- return 0; +-} +- +-static void __exit epxa_mtd_cleanup(void) +-{ +- if (mymtd) { +- if (nr_parts) +- del_mtd_partitions(mymtd); +- else +- del_mtd_device(mymtd); +- map_destroy(mymtd); +- } +- if (epxa_map.map_priv_1) { +- iounmap((void *)epxa_map.map_priv_1); +- epxa_map.map_priv_1 = 0; +- } +-} +- +- +-/* +- * This will do for now, once we decide which bootldr we're finally +- * going to use then we'll remove this function and do it properly +- * +- * Partions are currently (as offsets from base of flash): +- * 0x00000000 - 0x003FFFFF - bootloader (!) +- * 0x00400000 - 0x00FFFFFF - Flashdisk +- */ +- +-static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts) +-{ +- struct mtd_partition *parts; +- int ret, i; +- int npartitions = 0; +- char *names; +- const char *name = "jffs"; +- +- printk("Using default partitions for %s\n",BOARD_NAME); +- npartitions=1; +- parts = kmalloc(npartitions*sizeof(*parts)+strlen(name), GFP_KERNEL); +- memzero(parts,npartitions*sizeof(*parts)+strlen(name)); +- if (!parts) { +- ret = -ENOMEM; +- goto out; +- } +- i=0; +- names = (char *)&parts[npartitions]; +- parts[i].name = names; +- names += strlen(name) + 1; +- strcpy(parts[i].name, name); +- +-#ifdef CONFIG_EPXA10DB +- parts[i].size = FLASH_SIZE-0x00400000; +- parts[i].offset = 0x00400000; +-#else +- parts[i].size = FLASH_SIZE-0x00180000; +- parts[i].offset = 0x00180000; +-#endif +- +- out: +- *pparts = parts; +- return npartitions; +-} +- +- +-module_init(epxa_mtd_init); +-module_exit(epxa_mtd_cleanup); +- +-MODULE_AUTHOR("Clive Davies"); +-MODULE_DESCRIPTION("Altera epxa mtd flash map"); +-MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/mtd/maps/neponset-flash.c linux-2.4.26-vrs1/drivers/mtd/maps/neponset-flash.c +--- linux-2.4.26/drivers/mtd/maps/neponset-flash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/mtd/maps/neponset-flash.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,109 @@ ++/* ++ * Flash memory access on SA11x0 based devices ++ * ++ * (C) 2000 Nicolas Pitre ++ * ++ * $Id: neponset-flash.c,v 1.18 2001/07/14 00:59:17 thockin Exp $ ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++static __u8 read8(struct map_info *map, unsigned long ofs) ++{ ++ return readb(map->map_priv_1 + ofs); ++} ++ ++static __u16 read16(struct map_info *map, unsigned long ofs) ++{ ++ return readw(map->map_priv_1 + ofs); ++} ++ ++static __u32 read32(struct map_info *map, unsigned long ofs) ++{ ++ return readl(map->map_priv_1 + ofs); ++} ++ ++static void copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++{ ++ memcpy_fromio(to, map->map_priv_1 + from, len); ++} ++ ++static void write8(struct map_info *map, __u8 d, unsigned long adr) ++{ ++ writeb(d, map->map_priv_1 + adr); ++} ++ ++static void write16(struct map_info *map, __u16 d, unsigned long adr) ++{ ++ writew(d, map->map_priv_1 + adr); ++} ++ ++static void write32(struct map_info *map, __u32 d, unsigned long adr) ++{ ++ writel(d, map->map_priv_1 + adr); ++} ++ ++static void copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++{ ++ memcpy_toio(map->map_priv_1 + to, from, len); ++} ++ ++#define MAX_SZ (32 * 1024 * 1024) ++ ++static struct map_info neponset_map = { ++ name: "Neponset", ++ size: MAX_SZ, ++ buswidth: 4, ++ read8: read8, ++ read16: read16, ++ read32: read32, ++ copy_from: copy_from, ++ write8: write8, ++ write16: write16, ++ write32: write32, ++ copy_to: copy_to, ++}; ++ ++extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); ++extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts); ++ ++static struct mtd_info *neponset_mtd; ++ ++int __init neponset_mtd_init(void) ++{ ++ if (!machine_is_assabet() || !machine_has_neponset()) ++ return -ENODEV; ++ ++ neponset_map.map_priv_1 = (unsigned int)ioremap(0x08000000, MAX_SZ); ++ if (!neponset_map.map_priv_1) ++ return -ENOMEM; ++ ++ neponset_mtd = do_map_probe("cfi_probe", &neponset_map); ++ if (!neponset_mtd) ++ return -ENXIO; ++ neponset_mtd->module = THIS_MODULE; ++ add_mtd_device(neponset_mtd); ++ return 0; ++} ++ ++static void __exit neponset_mtd_cleanup(void) ++{ ++ if (neponset_mtd) ++ map_destroy(neponset_mtd); ++ if (neponset_map.map_priv_1) ++ iounmap((void *)neponset_map.map_priv_1); ++} ++ ++module_init(neponset_mtd_init); ++module_exit(neponset_mtd_cleanup); +diff -urN linux-2.4.26/drivers/mtd/maps/sa1100-flash.c linux-2.4.26-vrs1/drivers/mtd/maps/sa1100-flash.c +--- linux-2.4.26/drivers/mtd/maps/sa1100-flash.c 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/mtd/maps/sa1100-flash.c 2004-01-14 21:32:26.000000000 +0000 +@@ -97,6 +97,32 @@ + * entries. Thanks. + */ + ++#ifdef CONFIG_SA1100_ADSAGC ++#define ADSAGC_FLASH_SIZE 0x02000000 ++static struct mtd_partition adsagc_partitions[] = { ++ { ++ name: "bootROM", ++ size: 0x80000, ++ offset: 0, ++ mask_flags: MTD_WRITEABLE, /* force read-only */ ++ }, { ++ name: "zImage", ++ size: 0x100000, ++ offset: MTDPART_OFS_APPEND, ++ mask_flags: MTD_WRITEABLE, /* force read-only */ ++ }, { ++ name: "ramdisk.gz", ++ size: 0x300000, ++ offset: MTDPART_OFS_APPEND, ++ mask_flags: MTD_WRITEABLE, /* force read-only */ ++ }, { ++ name: "User FS", ++ size: MTDPART_SIZ_FULL, ++ offset: MTDPART_OFS_APPEND, ++ } ++}; ++#endif ++ + #ifdef CONFIG_SA1100_ADSBITSY + #define ADSBITSY_FLASH_SIZE 0x02000000 + static struct mtd_partition adsbitsy_partitions[] = { +@@ -123,6 +149,32 @@ + }; + #endif + ++#ifdef CONFIG_SA1100_ADSBITSYPLUS ++#define ADSBITSYPLUS_FLASH_SIZE 0x02000000 ++static struct mtd_partition adsbitsyplus_partitions[] = { ++ { ++ name: "bootROM", ++ size: 0x80000, ++ offset: 0, ++ mask_flags: MTD_WRITEABLE, /* force read-only */ ++ }, { ++ name: "zImage", ++ size: 0x100000, ++ offset: MTDPART_OFS_APPEND, ++ mask_flags: MTD_WRITEABLE, /* force read-only */ ++ }, { ++ name: "ramdisk.gz", ++ size: 0x300000, ++ offset: MTDPART_OFS_APPEND, ++ mask_flags: MTD_WRITEABLE, /* force read-only */ ++ }, { ++ name: "User FS", ++ size: MTDPART_SIZ_FULL, ++ offset: MTDPART_OFS_APPEND, ++ } ++}; ++#endif ++ + #ifdef CONFIG_SA1100_ASSABET + /* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */ + #define ASSABET4_FLASH_SIZE 0x00400000 +@@ -438,7 +490,7 @@ + #endif + + #ifdef CONFIG_SA1100_GRAPHICSMASTER +-#define GRAPHICSMASTER_FLASH_SIZE 0x01000000 ++#define GRAPHICSMASTER_FLASH_SIZE 0x02000000 + static struct mtd_partition graphicsmaster_partitions[] = { + { + name: "zImage", +@@ -507,6 +559,38 @@ + } + #endif + ++#ifdef CONFIG_SA1100_HACKKIT ++#define HACKKIT_FLASH_SIZE 0x01000000 ++static struct mtd_partition hackkit_partitions[] = { ++ { ++ name: "BLOB", ++ size: 0x00040000, ++ offset: 0x00000000, ++ mask_flags: MTD_WRITEABLE, /* force read-only */ ++ }, { ++ name: "config", ++ size: 0x00040000, ++ offset: MTDPART_OFS_APPEND, ++ }, { ++ name: "kernel", ++ size: 0x00100000, ++ offset: MTDPART_OFS_APPEND, ++ }, { ++ name: "initrd", ++ size: 0x00180000, ++ offset: MTDPART_OFS_APPEND, ++ }, { ++ name: "rootfs", ++ size: 0x700000, ++ offset: MTDPART_OFS_APPEND, ++ }, { ++ name: "data", ++ size: MTDPART_SIZ_FULL, ++ offset: MTDPART_OFS_APPEND, ++ } ++}; ++#endif ++ + #ifdef CONFIG_SA1100_HUW_WEBPANEL + #define HUW_WEBPANEL_FLASH_SIZE 0x01000000 + static struct mtd_partition huw_webpanel_partitions[] = { +@@ -555,12 +639,12 @@ + offset: 0x00540000, + }, { + name: "JORNADA720 usr local", +- size: 0 /* will expand to the end of the flash */ ++ size: 0, /* will expand to the end of the flash */ + offset: 0x00d00000, + } + }; + +-static void jornada720_set_vpp(int vpp) ++static void jornada720_set_vpp(struct map_info *map, int vpp) + { + if (vpp) + PPSR |= 0x80; +@@ -571,6 +655,27 @@ + + #endif + ++#ifdef CONFIG_SA1100_NANOENGINE ++/* nanoEngine has one 28F320B3B Flash part in bank 0: */ ++#define NANOENGINE_FLASH_SIZE 0x00400000 ++static struct mtd_partition nanoengine_partitions[] = { ++ { ++ name: "nanoEngine boot firmware and parameter table", ++ size: 0x00010000, /* 32K */ ++ offset: 0x00000000, ++ mask_flags: MTD_WRITEABLE, /* force read-only */ ++ },{ ++ name: "kernel/initrd reserved", ++ size: 0x002f0000, ++ offset: 0x00010000, ++ },{ ++ name: "experimental filesystem allocation", ++ size: 0x00100000, ++ offset: 0x00300000, ++ } ++}; ++#endif ++ + #ifdef CONFIG_SA1100_PANGOLIN + #define PANGOLIN_FLASH_SIZE 0x04000000 + static struct mtd_partition pangolin_partitions[] = { +@@ -699,6 +804,32 @@ + }; + #endif /* CONFIG_SA1100_SIMPAD */ + ++#ifdef CONFIG_SA1100_SIMPUTER ++#define SIMPUTER_FLASH_SIZE 0x02000000 ++static struct mtd_partition simputer_partitions[] = { ++ { ++ name: "blob+logo", ++ offset: 0, ++ size: 0x00040000 ++ }, ++ { ++ name: "kernel", ++ offset: MTDPART_OFS_APPEND, ++ size: 0x000C0000 ++ }, ++ { ++ name: "/(cramfs)", ++ offset: MTDPART_OFS_APPEND, ++ size: 0x00200000 ++ }, ++ { ++ name: "/usr/local(jffs2)", ++ offset: MTDPART_OFS_APPEND, ++ size: MTDPART_SIZ_FULL /* expand till the end */ ++ } ++}; ++#endif ++ + #ifdef CONFIG_SA1100_STORK + #define STORK_FLASH_SIZE 0x02000000 + static struct mtd_partition stork_partitions[] = { +@@ -766,7 +897,7 @@ + #endif + + extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +-extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts); ++extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); + + static struct mtd_partition *parsed_parts; + static struct mtd_info *mymtd; +@@ -787,6 +918,14 @@ + */ + part_type = "static"; + ++#ifdef CONFIG_SA1100_ADSAGC ++ if (machine_is_adsagc()) { ++ parts = adsagc_partitions; ++ nb_parts = ARRAY_SIZE(adsagc_partitions); ++ sa1100_map.size = ADSAGC_FLASH_SIZE; ++ sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; ++ } ++#endif + #ifdef CONFIG_SA1100_ADSBITSY + if (machine_is_adsbitsy()) { + parts = adsbitsy_partitions; +@@ -795,6 +934,14 @@ + sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; + } + #endif ++#ifdef CONFIG_SA1100_ADSBITSYPLUS ++ if (machine_is_adsbitsyplus()) { ++ parts = adsbitsyplus_partitions; ++ nb_parts = ARRAY_SIZE(adsbitsyplus_partitions); ++ sa1100_map.size = ADSBITSYPLUS_FLASH_SIZE; ++ sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; ++ } ++#endif + #ifdef CONFIG_SA1100_ASSABET + if (machine_is_assabet()) { + parts = assabet_partitions; +@@ -869,6 +1016,13 @@ + sa1100_map.set_vpp = h3600_set_vpp; + } + #endif ++#ifdef CONFIG_SA1100_HACKKIT ++ if (machine_is_hackkit()) { ++ parts = hackkit_partitions; ++ nb_parts = ARRAY_SIZE(hackkit_partitions); ++ sa1100_map.size = HACKKIT_FLASH_SIZE; ++ } ++#endif + #ifdef CONFIG_SA1100_HUW_WEBPANEL + if (machine_is_huw_webpanel()) { + parts = huw_webpanel_partitions; +@@ -884,6 +1038,13 @@ + sa1100_map.set_vpp = jornada720_set_vpp; + } + #endif ++#ifdef CONFIG_SA1100_NANOENGINE ++ if (machine_is_nanoengine()) { ++ parts = nanoengine_partitions; ++ nb_parts = ARRAY_SIZE(nanoengine_partitions); ++ sa1100_map.size = NANOENGINE_FLASH_SIZE; ++ } ++#endif + #ifdef CONFIG_SA1100_PANGOLIN + if (machine_is_pangolin()) { + parts = pangolin_partitions; +@@ -919,6 +1080,13 @@ + sa1100_map.size = SIMPAD_FLASH_SIZE; + } + #endif ++#ifdef CONFIG_SA1100_SIMPUTER ++ if (machine_is_simputer()) { ++ parts = simputer_partitions; ++ nb_parts = ARRAY_SIZE(simputer_partitions); ++ sa1100_map.size = SIMPUTER_FLASH_SIZE; ++ } ++#endif + #ifdef CONFIG_SA1100_STORK + if (machine_is_stork()) { + parts = stork_partitions; +@@ -953,7 +1121,9 @@ + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8); +- mymtd = do_map_probe("cfi_probe", &sa1100_map); ++ mymtd = do_map_probe("jedec_probe", &sa1100_map); ++ if (!mymtd) ++ mymtd = do_map_probe("cfi_probe", &sa1100_map); + ret = -ENXIO; + if (!mymtd) + goto out_err; +diff -urN linux-2.4.26/drivers/mtd/nand/Config.in linux-2.4.26-vrs1/drivers/mtd/nand/Config.in +--- linux-2.4.26/drivers/mtd/nand/Config.in 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/mtd/nand/Config.in 2004-04-10 12:50:22.000000000 +0100 +@@ -33,4 +33,8 @@ + fi + fi + ++if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then ++ dep_tristate ' SmartMedia Card on Atmel AT91RM9200' CONFIG_MTD_AT91_SMARTMEDIA $CONFIG_MTD_NAND ++fi ++ + endmenu +diff -urN linux-2.4.26/drivers/net/Config.in linux-2.4.26-vrs1/drivers/net/Config.in +--- linux-2.4.26/drivers/net/Config.in 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/net/Config.in 2004-04-18 21:47:50.000000000 +0100 +@@ -30,9 +30,15 @@ + if [ "$CONFIG_ARCH_ACORN" = "y" ]; then + source drivers/acorn/net/Config.in + fi ++ if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then ++ tristate ' AT91RM9200 Ethernet support' CONFIG_AT91_ETHER ++ if [ "$CONFIG_AT91_ETHER" = "y" -o "$CONFIG_AT91_ETHER" = "m" ]; then ++ bool ' RMII interface? ' CONFIG_AT91_ETHER_RMII ++ fi ++ fi + fi + if [ "$CONFIG_ARCH_CAMELOT" = "y" ]; then +- tristate ' Altera Ether00 support' CONFIG_ETHER00 ++ tristate ' Altera Ether00 support' CONFIG_ETHER00 + fi + if [ "$CONFIG_PPC" = "y" ]; then + dep_tristate ' MACE (Power Mac ethernet) support' CONFIG_MACE $CONFIG_ALL_PPC +diff -urN linux-2.4.26/drivers/net/Makefile linux-2.4.26-vrs1/drivers/net/Makefile +--- linux-2.4.26/drivers/net/Makefile 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/net/Makefile 2004-04-18 21:47:50.000000000 +0100 +@@ -244,6 +244,7 @@ + # non-drivers/net drivers who want mii lib + obj-$(CONFIG_PCMCIA_SMC91C92) += mii.o + obj-$(CONFIG_USB_USBNET) += mii.o ++obj-$(CONFIG_AT91_ETHER) += mii.o + + ifeq ($(CONFIG_ARCH_ACORN),y) + mod-subdirs += ../acorn/net +@@ -268,4 +269,3 @@ + + rcpci.o: $(rcpci-objs) + $(LD) -r -o $@ $(rcpci-objs) +- +diff -urN linux-2.4.26/drivers/net/am79c961a.c linux-2.4.26-vrs1/drivers/net/am79c961a.c +--- linux-2.4.26/drivers/net/am79c961a.c 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/net/am79c961a.c 2004-01-14 21:32:26.000000000 +0000 +@@ -54,25 +54,36 @@ + #ifdef __arm__ + static void write_rreg(u_long base, u_int reg, u_int val) + { +- __asm__("str%?h %1, [%2] @ NET_RAP +- str%?h %0, [%2, #-4] @ NET_RDP +- " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); ++ __asm__("str%?h %1, [%2] @ NET_RAP\n\t" ++ "str%?h %0, [%2, #-4] @ NET_RDP" ++ : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); + } + + static inline unsigned short read_rreg(u_long base_addr, u_int reg) + { + unsigned short v; +- __asm__("str%?h %1, [%2] @ NET_RAP +- ldr%?h %0, [%2, #-4] @ NET_RDP +- " : "=r" (v): "r" (reg), "r" (ISAIO_BASE + 0x0464)); ++ __asm__("str%?h %1, [%2] @ NET_RAP\n\t" ++ "ldr%?h %0, [%2, #-4] @ NET_RDP" ++ : "=r" (v): "r" (reg), "r" (ISAIO_BASE + 0x0464)); + return v; + } + + static inline void write_ireg(u_long base, u_int reg, u_int val) + { +- __asm__("str%?h %1, [%2] @ NET_RAP +- str%?h %0, [%2, #8] @ NET_IDP +- " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); ++ __asm__("str%?h %1, [%2] @ NET_RAP\n\t" ++ "str%?h %0, [%2, #8] @ NET_IDP" ++ : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); ++} ++ ++static inline unsigned short read_ireg(u_long base_addr, u_int reg) ++{ ++ u_short v; ++ __asm__( ++ "str%?h %1, [%2] @ NAT_RAP\n\t" ++ "str%?h %0, [%2, #8] @ NET_IDP\n\t" ++ : "=r" (v) ++ : "r" (reg), "r" (ISAIO_BASE + 0x0464)); ++ return v; + } + + #define am_writeword(dev,off,val) __raw_writew(val, ISAMEM_BASE + ((off) << 1)) +@@ -91,16 +102,16 @@ + } + while (length > 8) { + unsigned int tmp, tmp2; +- __asm__ __volatile__(" +- ldm%?ia %1!, {%2, %3} +- str%?h %2, [%0], #4 +- mov%? %2, %2, lsr #16 +- str%?h %2, [%0], #4 +- str%?h %3, [%0], #4 +- mov%? %3, %3, lsr #16 +- str%?h %3, [%0], #4 +- " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2) +- : "0" (offset), "1" (buf)); ++ __asm__ __volatile__( ++ "ldm%?ia %1!, {%2, %3}\n\t" ++ "str%?h %2, [%0], #4\n\t" ++ "mov%? %2, %2, lsr #16\n\t" ++ "str%?h %2, [%0], #4\n\t" ++ "str%?h %3, [%0], #4\n\t" ++ "mov%? %3, %3, lsr #16\n\t" ++ "str%?h %3, [%0], #4" ++ : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2) ++ : "0" (offset), "1" (buf)); + length -= 8; + } + while (length > 0) { +@@ -118,36 +129,36 @@ + length = (length + 1) & ~1; + if ((int)buf & 2) { + unsigned int tmp; +- __asm__ __volatile__(" +- ldr%?h %2, [%0], #4 +- str%?b %2, [%1], #1 +- mov%? %2, %2, lsr #8 +- str%?b %2, [%1], #1 +- " : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf)); ++ __asm__ __volatile__( ++ "ldr%?h %2, [%0], #4\n\t" ++ "str%?b %2, [%1], #1\n\t" ++ "mov%? %2, %2, lsr #8\n\t" ++ "str%?b %2, [%1], #1" ++ : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf)); + length -= 2; + } + while (length > 8) { + unsigned int tmp, tmp2, tmp3; +- __asm__ __volatile__(" +- ldr%?h %2, [%0], #4 +- ldr%?h %3, [%0], #4 +- orr%? %2, %2, %3, lsl #16 +- ldr%?h %3, [%0], #4 +- ldr%?h %4, [%0], #4 +- orr%? %3, %3, %4, lsl #16 +- stm%?ia %1!, {%2, %3} +- " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3) +- : "0" (offset), "1" (buf)); ++ __asm__ __volatile__( ++ "ldr%?h %2, [%0], #4\n\t" ++ "ldr%?h %3, [%0], #4\n\t" ++ "orr%? %2, %2, %3, lsl #16\n\t" ++ "ldr%?h %3, [%0], #4\n\t" ++ "ldr%?h %4, [%0], #4\n\t" ++ "orr%? %3, %3, %4, lsl #16\n\t" ++ "stm%?ia %1!, {%2, %3}" ++ : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3) ++ : "0" (offset), "1" (buf)); + length -= 8; + } + while (length > 0) { + unsigned int tmp; +- __asm__ __volatile__(" +- ldr%?h %2, [%0], #4 +- str%?b %2, [%1], #1 +- mov%? %2, %2, lsr #8 +- str%?b %2, [%1], #1 +- " : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf)); ++ __asm__ __volatile__( ++ "ldr%?h %2, [%0], #4\n\t" ++ "str%?b %2, [%1], #1\n\t" ++ "mov%? %2, %2, lsr #8\n\t" ++ "str%?b %2, [%1], #1" ++ : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf)); + length -= 2; + } + } +@@ -254,9 +265,27 @@ + write_rreg (dev->base_addr, BASERXH, 0); + write_rreg (dev->base_addr, CSR0, CSR0_STOP); + write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO); ++ write_rreg (dev->base_addr, CSR4, CSR4_APAD_XMIT|CSR4_MFCOM|CSR4_RCVCCOM|CSR4_TXSTRTM|CSR4_JABM); + write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT); + } + ++static void am79c961_timer(unsigned long data) ++{ ++ struct net_device *dev = (struct net_device *)data; ++ struct dev_priv *priv = (struct dev_priv *)dev->priv; ++ unsigned int lnkstat, carrier; ++ ++ lnkstat = read_ireg(dev->base_addr, ISALED0) & ISALED0_LNKST; ++ carrier = netif_carrier_ok(dev); ++ ++ if (lnkstat && !carrier) ++ netif_carrier_on(dev); ++ else if (!lnkstat && carrier) ++ netif_carrier_off(dev); ++ ++ mod_timer(&priv->timer, jiffies + 5*HZ); ++} ++ + /* + * Open/initialize the board. + */ +@@ -274,6 +303,11 @@ + + am79c961_init_for_open(dev); + ++ netif_carrier_off(dev); ++ ++ priv->timer.expires = jiffies; ++ add_timer(&priv->timer); ++ + netif_start_queue(dev); + + return 0; +@@ -288,7 +322,10 @@ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned long flags; + ++ del_timer_sync(&priv->timer); ++ + netif_stop_queue(dev); ++ netif_carrier_off(dev); + + spin_lock_irqsave(priv->chip_lock, flags); + write_rreg (dev->base_addr, CSR0, CSR0_STOP); +@@ -413,15 +450,6 @@ + unsigned int hdraddr, bufaddr; + unsigned int head; + unsigned long flags; +- +- /* FIXME: I thought the 79c961 could do padding - RMK ??? */ +- if(length < ETH_ZLEN) +- { +- skb = skb_padto(skb, ETH_ZLEN); +- if(skb == NULL) +- return 0; +- length = ETH_ZLEN; +- } + + head = priv->txhead; + hdraddr = priv->txhdr + (head << 3); +@@ -431,7 +459,7 @@ + head = 0; + + am_writebuffer (dev, bufaddr, skb->data, length); +- am_writeword (dev, hdraddr + 4, -length); ++ am_writeword (dev, hdraddr + 4, -skb->len); + am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP); + priv->txhead = head; + +@@ -448,6 +476,8 @@ + if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN) + netif_stop_queue(dev); + ++ priv->stats.tx_bytes += skb->len; ++ + dev_kfree_skb(skb); + + return 0; +@@ -520,6 +550,7 @@ + am79c961_tx(struct net_device *dev, struct dev_priv *priv) + { + do { ++ signed short len; + u_int hdraddr; + u_int status; + +@@ -555,6 +586,8 @@ + continue; + } + priv->stats.tx_packets ++; ++ len = am_readword (dev, hdraddr + 4); ++ priv->stats.tx_bytes += -len; + } while (priv->txtail != priv->txhead); + + netif_wake_queue(dev); +@@ -565,17 +598,23 @@ + { + struct net_device *dev = (struct net_device *)dev_id; + struct dev_priv *priv = (struct dev_priv *)dev->priv; +- u_int status; ++ u_int status, n = 100; + +- status = read_rreg(dev->base_addr, CSR0); +- write_rreg(dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA)); +- +- if (status & CSR0_RINT) +- am79c961_rx(dev, priv); +- if (status & CSR0_TINT) +- am79c961_tx(dev, priv); +- if (status & CSR0_MISS) +- priv->stats.rx_dropped ++; ++ do { ++ status = read_rreg(dev->base_addr, CSR0); ++ write_rreg(dev->base_addr, CSR0, status & ++ (CSR0_IENA|CSR0_TINT|CSR0_RINT| ++ CSR0_MERR|CSR0_MISS|CSR0_CERR|CSR0_BABL)); ++ ++ if (status & CSR0_RINT) ++ am79c961_rx(dev, priv); ++ if (status & CSR0_TINT) ++ am79c961_tx(dev, priv); ++ if (status & CSR0_MISS) ++ priv->stats.rx_dropped ++; ++ if (status & CSR0_CERR) ++ mod_timer(&priv->timer, jiffies); ++ } while (--n && status & (CSR0_RINT | CSR0_TINT)); + } + + /* +@@ -587,10 +626,10 @@ + { + struct dev_priv *priv = (struct dev_priv *)dev->priv; + +- spin_lock_irq(priv->chip_lock); ++ spin_lock_irq(&priv->chip_lock); + write_rreg (dev->base_addr, CSR0, CSR0_STOP); + write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); +- spin_unlock_irq(priv->chip_lock); ++ spin_unlock_irq(&priv->chip_lock); + + am79c961_ramtest(dev, 0x66); + am79c961_ramtest(dev, 0x99); +@@ -655,6 +694,11 @@ + printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]); + } + ++ spin_lock_init(&priv->chip_lock); ++ init_timer(&priv->timer); ++ priv->timer.data = (unsigned long)dev; ++ priv->timer.function = am79c961_timer; ++ + if (am79c961_hw_init(dev)) + goto release; + +diff -urN linux-2.4.26/drivers/net/am79c961a.h linux-2.4.26-vrs1/drivers/net/am79c961a.h +--- linux-2.4.26/drivers/net/am79c961a.h 2000-09-18 23:15:22.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/net/am79c961a.h 2004-01-14 21:32:26.000000000 +0000 +@@ -58,6 +58,18 @@ + #define CSR3_BABLM 0x4000 + #define CSR3_MASKALL 0x5F00 + ++#define CSR4 4 ++#define CSR4_JABM 0x0001 ++#define CSR4_JAB 0x0002 ++#define CSR4_TXSTRTM 0x0004 ++#define CSR4_TXSTRT 0x0008 ++#define CSR4_RCVCCOM 0x0010 ++#define CSR4_RCVCCO 0x0020 ++#define CSR4_MFCOM 0x0100 ++#define CSR4_MFCO 0x0200 ++#define CSR4_ASTRP_RCV 0x0400 ++#define CSR4_APAD_XMIT 0x0800 ++ + #define CTRL1 5 + #define CTRL1_SPND 0x0001 + +@@ -93,6 +105,8 @@ + #define SIZERXR 76 + #define SIZETXR 78 + ++#define CSR_MFC 112 ++ + #define RMD_ENP 0x0100 + #define RMD_STP 0x0200 + #define RMD_CRC 0x0800 +@@ -112,6 +126,9 @@ + #define TST_UFLO 0x4000 + #define TST_BUFF 0x8000 + ++#define ISALED0 0x0004 ++#define ISALED0_LNKST 0x8000 ++ + struct dev_priv { + struct net_device_stats stats; + unsigned long rxbuffer[RX_BUFFERS]; +@@ -123,6 +140,7 @@ + unsigned long rxhdr; + unsigned long txhdr; + spinlock_t chip_lock; ++ struct timer_list timer; + }; + + extern int am79c961_probe (struct net_device *dev); +diff -urN linux-2.4.26/drivers/net/cirrus.c linux-2.4.26-vrs1/drivers/net/cirrus.c +--- linux-2.4.26/drivers/net/cirrus.c 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/net/cirrus.c 2004-01-14 21:32:26.000000000 +0000 +@@ -75,6 +75,7 @@ + typedef struct { + struct net_device_stats stats; + u16 txlen; ++ u16 txafter; /* Default is After5 (0) */ + } cirrus_t; + + typedef struct { +@@ -230,13 +231,19 @@ + cirrus_t *priv = (cirrus_t *) dev->priv; + u16 status; + ++ /* Tx start must be done with irq disabled ++ * else status can be wrong */ ++ disable_irq (dev->irq); ++ + netif_stop_queue (dev); + +- cirrus_write (dev,PP_TxCMD,TxStart (After5)); ++ cirrus_write (dev,PP_TxCMD,TxStart (priv->txafter)); + cirrus_write (dev,PP_TxLength,skb->len); + + status = cirrus_read (dev,PP_BusST); + ++ enable_irq (dev->irq); ++ + if ((status & TxBidErr)) { + printk (KERN_WARNING "%s: Invalid frame size %d!\n",dev->name,skb->len); + priv->stats.tx_errors++; +@@ -249,7 +256,6 @@ + printk (KERN_WARNING "%s: Transmit buffer not free!\n",dev->name); + priv->stats.tx_errors++; + priv->txlen = 0; +- /* FIXME: store skb and send it in interrupt handler */ + return (1); + } + +@@ -310,11 +316,18 @@ + } + if ((RegContent (status) & TxUnderrun)) { + priv->stats.tx_errors++; +- priv->stats.tx_fifo_errors++; ++ /* Shift start tx, if underruns come too often */ ++ switch (priv->stats.tx_fifo_errors++) { ++ case 3: priv->txafter = After381; break; ++ case 6: priv->txafter = After1021; break; ++ case 9: priv->txafter = AfterAll; break; ++ } ++ } ++ /* Wakeup only for tx events ! */ ++ if ((RegContent (status) & (TxUnderrun | Rdy4Tx))) { ++ priv->txlen = 0; ++ netif_wake_queue (dev); + } +- /* FIXME: if Rdy4Tx, transmit last sent packet (if any) */ +- priv->txlen = 0; +- netif_wake_queue (dev); + break; + + case TxCOL: +@@ -428,7 +441,7 @@ + else + cirrus_clear (dev,PP_RxCTL,PromiscuousA); + +- if ((dev->flags & IFF_ALLMULTI) && dev->mc_list) ++ if ((dev->flags & IFF_ALLMULTI) || dev->mc_list) + cirrus_set (dev,PP_RxCTL,MulticastA); + else + cirrus_clear (dev,PP_RxCTL,MulticastA); +diff -urN linux-2.4.26/drivers/net/cs89x0.c linux-2.4.26-vrs1/drivers/net/cs89x0.c +--- linux-2.4.26/drivers/net/cs89x0.c 2003-08-25 12:44:42.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/net/cs89x0.c 2004-01-14 21:39:05.000000000 +0000 +@@ -115,6 +115,7 @@ + + */ + ++#include + #include + #include + #include +@@ -427,18 +428,18 @@ + /* if they give us an odd I/O address, then do ONE write to + the address port, to get it back to address zero, where we + expect to find the EISA signature word. An IO with a base of 0x3 +- will skip the test for the ADD_PORT. */ ++ will skip the test for the ADD_PORT. */ + if (ioaddr & 1) { + if (net_debug > 1) + printk(KERN_INFO "%s: odd ioaddr 0x%x\n", dev->name, ioaddr); +- if ((ioaddr & 2) != 2) ++ if ((ioaddr & 2) != 2) + if ((inw((ioaddr & ~3)+ ADD_PORT) & ADD_MASK) != ADD_SIG) { + printk(KERN_ERR "%s: bad signature 0x%x\n", + dev->name, inw((ioaddr & ~3)+ ADD_PORT)); + retval = -ENODEV; + goto out2; + } +- ioaddr &= ~3; ++ ioaddr &= ~3; + outw(PP_ChipID, ioaddr + ADD_PORT); + } + printk("PP_addr=0x%x\n", inw(ioaddr + ADD_PORT)); +@@ -446,7 +447,7 @@ + if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG) { + printk(KERN_ERR "%s: incorrect signature 0x%x\n", + dev->name, inw(ioaddr + DATA_PORT)); +- retval = -ENODEV; ++ retval = -ENODEV; + goto out2; + } + +@@ -477,7 +478,7 @@ + dev->base_addr); + + reset_chip(dev); +- ++ + /* Here we read the current configuration of the chip. If there + is no Extended EEPROM then the idea is to not disturb the chip + configuration, it should have been correctly setup by automatic +diff -urN linux-2.4.26/drivers/net/ether00.c linux-2.4.26-vrs1/drivers/net/ether00.c +--- linux-2.4.26/drivers/net/ether00.c 2003-06-13 15:51:34.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/net/ether00.c 2004-01-14 21:32:26.000000000 +0000 +@@ -38,6 +38,7 @@ + #include + #include + ++static int ether00_get_ethernet_address(struct net_device* dev); + + MODULE_AUTHOR("Clive Davies"); + MODULE_DESCRIPTION("Altera Ether00 IP core driver"); +@@ -734,8 +735,11 @@ + int result,tmp; + struct net_priv* priv; + +- if (!is_valid_ether_addr(dev->dev_addr)) +- return -EINVAL; ++ if (!ether00_get_ethernet_address(dev)){ ++ printk("%s: Invalid ethernet MAC address. Please set using " ++ "ifconfig\n", dev->name); ++ return -EINVAL; ++ } + + dev->base_addr=(unsigned int)ioremap_nocache(base,SZ_4K); + +@@ -906,10 +910,9 @@ + } + + +-static void ether00_get_ethernet_address(struct net_device* dev) ++static int ether00_get_ethernet_address(struct net_device* dev) + { + struct mtd_info *mymtd=NULL; +- int i; + size_t retlen; + + /* +@@ -926,11 +929,7 @@ + #ifdef CONFIG_ARCH_CAMELOT + #ifdef CONFIG_MTD + /* get the mtd_info structure for the first mtd device*/ +- for(i=0;iname,"EPXA10DB flash")) +- break; +- } ++ mymtd=get_mtd_device(NULL,0); + + if(!mymtd || !mymtd->read_user_prot_reg){ + printk(KERN_WARNING "%s: Failed to read MAC address from flash\n",dev->name); +@@ -947,9 +946,7 @@ + #endif + #endif + +- if (!is_valid_ether_addr(dev->dev_addr)) +- printk("%s: Invalid ethernet MAC address. Please set using " +- "ifconfig\n", dev->name); ++ return (is_valid_ether_addr(dev->dev_addr)); + + } + +@@ -966,8 +963,6 @@ + dev->tx_timeout=ether00_tx_timeout; + dev->watchdog_timeo=TX_TIMEOUT; + +- ether00_get_ethernet_address(dev); +- + SET_MODULE_OWNER(dev); + return 0; + } +diff -urN linux-2.4.26/drivers/net/irda/Config.in linux-2.4.26-vrs1/drivers/net/irda/Config.in +--- linux-2.4.26/drivers/net/irda/Config.in 2004-02-27 20:03:26.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/net/irda/Config.in 2004-02-23 23:21:17.000000000 +0000 +@@ -40,7 +40,7 @@ + dep_tristate 'VIA IrCC (Experimental)' CONFIG_VIA_IRCC_FIR $CONFIG_IRDA + fi + if [ "$CONFIG_ARCH_SA1100" = "y" ]; then +- dep_tristate 'SA1100 Internal IR' CONFIG_SA1100_FIR $CONFIG_IRDA ++ dep_tristate 'SA1100 Internal IR' CONFIG_SA1100_FIR $CONFIG_IRDA $CONFIG_EXPERIMENTAL + fi + + endmenu +diff -urN linux-2.4.26/drivers/net/irda/sa1100_ir.c linux-2.4.26-vrs1/drivers/net/irda/sa1100_ir.c +--- linux-2.4.26/drivers/net/irda/sa1100_ir.c 2002-08-03 01:39:44.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/net/irda/sa1100_ir.c 2004-01-14 21:32:26.000000000 +0000 +@@ -38,11 +38,7 @@ + + #include + +-#ifndef CONFIG_SA1100_H3600 +-#define clr_h3600_egpio(x) do { } while (0) +-#define set_h3600_egpio(x) do { } while (0) +-#endif +- ++/* Yopy wants fixing */ + #ifndef GPIO_IRDA_FIR + #define GPIO_IRDA_FIR (0) + #endif +@@ -174,8 +170,8 @@ + + if (machine_is_assabet()) + ASSABET_BCR_clear(ASSABET_BCR_IRDA_FSEL); +- if (machine_is_h3600()) +- clr_h3600_egpio(EGPIO_H3600_IR_FSEL); ++ if (machine_is_h3xxx()) ++ clr_h3600_egpio(IPAQ_EGPIO_IR_FSEL); + if (machine_is_yopy()) + PPSR &= ~GPIO_IRDA_FIR; + +@@ -199,8 +195,8 @@ + + if (machine_is_assabet()) + ASSABET_BCR_set(ASSABET_BCR_IRDA_FSEL); +- if (machine_is_h3600()) +- set_h3600_egpio(EGPIO_H3600_IR_FSEL); ++ if (machine_is_h3xxx()) ++ set_h3600_egpio(IPAQ_EGPIO_IR_FSEL); + if (machine_is_yopy()) + PPSR |= GPIO_IRDA_FIR; + +@@ -246,10 +242,7 @@ + static inline int + sa1100_irda_set_power_h3600(struct sa1100_irda *si, unsigned int state) + { +- if (state) +- set_h3600_egpio(EGPIO_H3600_IR_ON); +- else +- clr_h3600_egpio(EGPIO_H3600_IR_ON); ++ assign_h3600_egpio( IPAQ_EGPIO_IR_ON, state ); + return 0; + } + +@@ -283,7 +276,7 @@ + + if (machine_is_assabet()) + ret = sa1100_irda_set_power_assabet(si, state); +- if (machine_is_h3600()) ++ if (machine_is_h3xxx()) + ret = sa1100_irda_set_power_h3600(si, state); + if (machine_is_yopy()) + ret = sa1100_irda_set_power_yopy(si, state); +@@ -727,11 +720,6 @@ + netif_wake_queue(dev); + } + +-/* +- * Note that we will never build up a backlog of frames; the protocol is a +- * half duplex protocol which basically means we transmit a frame, we +- * receive a frame, we transmit the next frame etc. +- */ + static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) + { + struct sa1100_irda *si = dev->priv; +@@ -758,6 +746,8 @@ + } + + if (!IS_FIR(si)) { ++ netif_stop_queue(dev); ++ + si->tx_buff.data = si->tx_buff.head; + si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, + si->tx_buff.truesize); +diff -urN linux-2.4.26/drivers/net/irda/w83977af_ir.c linux-2.4.26-vrs1/drivers/net/irda/w83977af_ir.c +--- linux-2.4.26/drivers/net/irda/w83977af_ir.c 2002-11-28 23:53:13.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/net/irda/w83977af_ir.c 2004-01-14 21:32:26.000000000 +0000 +@@ -205,7 +205,7 @@ + + /* FIXME: The HP HDLS-1100 does not support 1152000! */ + self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| +- IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); ++ IR_115200/*|IR_576000|IR_1152000|(IR_4000000 << 8)*/; + + /* The HP HDLS-1100 needs 1 ms according to the specs */ + self->qos.min_turn_time.bits = qos_mtt_bits; +@@ -1341,7 +1341,7 @@ + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; +- goto out; ++ break; + } + w83977af_change_speed(self, irq->ifr_baudrate); + break; +diff -urN linux-2.4.26/drivers/net/smc9194.c linux-2.4.26-vrs1/drivers/net/smc9194.c +--- linux-2.4.26/drivers/net/smc9194.c 2003-06-13 15:51:35.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/net/smc9194.c 2004-01-14 21:32:26.000000000 +0000 +@@ -12,8 +12,8 @@ + . AUI/TP selection ( mine has 10Base2/10BaseT select ) + . + . Arguments: +- . io = for the base address +- . irq = for the IRQ ++ . io = for the base address ++ . irq = for the IRQ + . ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 ) + . + . author: +@@ -51,12 +51,21 @@ + . allocation + . 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet + . 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ" ++ . 06/23/01 Russell King Separate out IO functions for different bus ++ . types. ++ . Use dev->name instead of CARDNAME for printk ++ . Add ethtool support, full duplex support ++ . Add LAN91C96 support. + . 11/08/01 Matt Domsch Use common crc32 function + ----------------------------------------------------------------------------*/ + ++#define DRV_NAME "smc9194" ++#define DRV_VERSION "0.15" ++ + static const char version[] = +- "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)\n"; ++ DRV_NAME ".c:v" DRV_VERSION " 12/15/00 by Erik Stahlman (erik@vt.edu)\n"; + ++#include + #include + #include + #include +@@ -69,16 +78,26 @@ + #include + #include + #include ++#include + #include + #include +-#include +-#include + #include ++#include + + #include + #include + #include + ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_ARCH_SA1100 ++#include ++#include ++#endif ++ + #include "smc9194.h" + /*------------------------------------------------------------------------ + . +@@ -152,29 +171,27 @@ + -------------------------------------------------------------------------*/ + #define CARDNAME "SMC9194" + ++static const char *chip_ids[15] = { ++ NULL, ++ NULL, ++ NULL, ++ "SMC91C90/91C92", /* 3 */ ++ "SMC91C94/91C96", /* 4 */ ++ "SMC91C95", /* 5 */ ++ NULL, ++ "SMC91C100", /* 7 */ ++ "SMC91C100FD", /* 8 */ ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ NULL ++}; + +-/* store this information for the driver.. */ +-struct smc_local { +- /* +- these are things that the kernel wants me to keep, so users +- can find out semi-useless statistics of how well the card is +- performing +- */ +- struct net_device_stats stats; +- +- /* +- If I have to wait until memory is available to send +- a packet, I will store the skbuff here, until I get the +- desired memory. Then, I'll send it out and free it. +- */ +- struct sk_buff * saved_skb; +- +- /* +- . This keeps track of how many packets that I have +- . sent out. When an TX_EMPTY interrupt comes, I know +- . that all of these have been sent. +- */ +- int packets_waiting; ++static const char * interfaces[2] = { ++ "TP", ++ "AUI" + }; + + +@@ -202,6 +219,11 @@ + static int smc_open(struct net_device *dev); + + /* ++ . This handles the ethtool interface ++*/ ++static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); ++ ++/* + . Our watchdog timed out. Called by the networking layer + */ + static void smc_timeout(struct net_device *dev); +@@ -217,11 +239,11 @@ + . This routine allows the proc file system to query the driver's + . statistics. + */ +-static struct net_device_stats * smc_query_statistics( struct net_device *dev); ++static struct net_device_stats * smc_query_statistics(struct net_device *dev); + + /* +- . Finally, a call to set promiscuous mode ( for TCPDUMP and related +- . programs ) and multicast modes. ++ . Finally, a call to set promiscuous mode (for TCPDUMP and related ++ . programs) and multicast modes. + */ + static void smc_set_multicast_list(struct net_device *dev); + +@@ -240,12 +262,12 @@ + . This is a separate procedure to handle the receipt of a packet, to + . leave the interrupt code looking slightly cleaner + */ +-static inline void smc_rcv( struct net_device *dev ); ++static inline void smc_rcv(struct net_device *dev); + /* + . This handles a TX interrupt, which is only called when an error + . relating to a packet is sent. + */ +-static inline void smc_tx( struct net_device * dev ); ++static inline void smc_tx(struct net_device * dev); + + /* + ------------------------------------------------------------ +@@ -261,39 +283,287 @@ + */ + static int smc_probe(struct net_device *dev, int ioaddr); + +-/* +- . A rather simple routine to print out a packet for debugging purposes. +-*/ +-#if SMC_DEBUG > 2 +-static void print_packet( byte *, int ); +-#endif +- +-#define tx_done(dev) 1 +- + /* this is called to actually send the packet to the chip */ +-static void smc_hardware_send_packet( struct net_device * dev ); ++static void smc_hardware_send_packet(struct net_device * dev); + + /* Since I am not sure if I will have enough room in the chip's ram + . to store the packet, I call this routine, which either sends it + . now, or generates an interrupt when the card is ready for the + . packet */ +-static int smc_wait_to_send_packet( struct sk_buff * skb, struct net_device *dev ); ++static int smc_wait_to_send_packet(struct sk_buff * skb, struct net_device *dev); + + /* this does a soft reset on the device */ +-static void smc_reset( int ioaddr ); ++static void smc_reset(struct net_device *dev); + + /* Enable Interrupts, Receive, and Transmit */ +-static void smc_enable( int ioaddr ); ++static void smc_enable(struct net_device *dev); + + /* this puts the device in an inactive state */ +-static void smc_shutdown( int ioaddr ); ++static void smc_shutdown(struct net_device *dev); + + /* This routine will find the IRQ of the driver if one is not + . specified in the input to the device. */ +-static int smc_findirq( int ioaddr ); ++static int smc_findirq(struct net_device *dev); ++ ++#ifndef CONFIG_ASSABET_NEPONSET ++/* ++ * These functions allow us to handle IO addressing as we wish - this ++ * ethernet controller can be connected to a variety of busses. Some ++ * busses do not support 16 bit or 32 bit transfers. --rmk ++ */ ++static inline u8 smc_inb(u_int base, u_int reg) ++{ ++ return inb(base + reg); ++} ++ ++static inline u16 smc_inw(u_int base, u_int reg) ++{ ++ return inw(base + reg); ++} ++ ++static inline void smc_ins(u_int base, u_int reg, u8 *data, u_int len) ++{ ++ u_int port = base + reg; ++#ifdef USE_32_BIT ++ /* QUESTION: Like in the TX routine, do I want ++ to send the DWORDs or the bytes first, or some ++ mixture. A mixture might improve already slow PIO ++ performance */ ++ PRINTK3((" Reading %d dwords (and %d bytes) \n", ++ len >> 2, len & 3)); ++ insl(port, data, len >> 2); ++ /* read the left over bytes */ ++ insb(port, data + (len & ~3), len & 3); ++#else ++ PRINTK3((" Reading %d words and %d byte(s) \n", ++ len >> 1, len & 1)); ++ insw(port, data, len >> 1); ++ if (len & 1) { ++ data += len & ~1; ++ *data = inb(port); ++ } ++#endif ++} ++ ++static inline void smc_outb(u8 val, u_int base, u_int reg) ++{ ++ outb(val, base + reg); ++} ++ ++static inline void smc_outw(u16 val, u_int base, u_int reg) ++{ ++ outw(val, base + reg); ++} ++ ++static inline void smc_outl(u32 val, u_int base, u_int reg) ++{ ++ u_int port = base + reg; ++#ifdef USE_32_BIT ++ outl(val, port); ++#else ++ outw(val, port); ++ outw(val >> 16, port); ++#endif ++} ++ ++static inline void smc_outs(u_int base, u_int reg, u8 *data, u_int len) ++{ ++ u_int port = base + reg; ++#ifdef USE_32_BIT ++ if (len & 2) { ++ outsl(port, data, len >> 2); ++ outw(*((word *)(data + (len & ~3))), port); ++ } ++ else ++ outsl(port, data, len >> 2); ++#else ++ outsw(port, data, len >> 1); ++#endif ++} ++ ++ ++/*------------------------------------------------------------------------- ++ . I define some macros to make it easier to do somewhat common ++ . or slightly complicated, repeated tasks. ++ --------------------------------------------------------------------------*/ ++ ++/* select a register bank, 0 to 3 */ ++ ++#define SMC_SELECT_BANK(x) \ ++ { \ ++ smc_outw(x, ioaddr, BANK_SELECT); \ ++ } ++ ++/* define a small delay for the reset */ ++#define SMC_DELAY() \ ++ { \ ++ smc_inw(ioaddr, RCR); \ ++ smc_inw(ioaddr, RCR); \ ++ smc_inw(ioaddr, RCR); \ ++ } ++ ++/* this enables an interrupt in the interrupt mask register */ ++#define SMC_ENABLE_INT(x) \ ++ { \ ++ byte mask; \ ++ mask = smc_inb(ioaddr, INT_MASK); \ ++ mask |= (x); \ ++ smc_outb(mask, ioaddr, INT_MASK); \ ++ } ++ ++/* this sets the absolutel interrupt mask */ ++#define SMC_SET_INT(x) \ ++ { \ ++ smc_outw((x), INT_MASK); \ ++ } ++ ++#else ++ ++#undef SMC_IO_EXTENT ++#define SMC_IO_EXTENT (16 << 2) ++ ++/* ++ * These functions allow us to handle IO addressing as we wish - this ++ * ethernet controller can be connected to a variety of busses. Some ++ * busses do not support 16 bit or 32 bit transfers. --rmk ++ */ ++static inline u8 smc_inb(u_int base, u_int reg) ++{ ++ u_int port = base + reg * 4; ++ ++ return readb(port); ++} ++ ++static inline u16 smc_inw(u_int base, u_int reg) ++{ ++ u_int port = base + reg * 4; ++ ++ return readb(port) | readb(port + 4) << 8; ++} ++ ++static inline void smc_ins(u_int base, u_int reg, u8 *data, u_int len) ++{ ++ u_int port = base + reg * 4; ++ ++ insb(port, data, len); ++} ++ ++static inline void smc_outb(u8 val, u_int base, u_int reg) ++{ ++ u_int port = base + reg * 4; ++ ++ writeb(val, port); ++} ++ ++static inline void smc_outw(u16 val, u_int base, u_int reg) ++{ ++ u_int port = base + reg * 4; ++ ++ writeb(val, port); ++ writeb(val >> 8, port + 4); ++} ++ ++static inline void smc_outl(u32 val, u_int base, u_int reg) ++{ ++ u_int port = base + reg * 4; ++ ++ writeb(val, port); ++ writeb(val >> 8, port + 4); ++ writeb(val >> 16, port + 8); ++ writeb(val >> 24, port + 12); ++} ++ ++static inline void smc_outs(u_int base, u_int reg, u8 *data, u_int len) ++{ ++ u_int port = base + reg * 4; ++ ++ outsb(port, data, len & ~1); ++} ++ ++/*------------------------------------------------------------------------- ++ . I define some macros to make it easier to do somewhat common ++ . or slightly complicated, repeated tasks. ++ --------------------------------------------------------------------------*/ ++ ++/* select a register bank, 0 to 3 */ ++ ++#define SMC_SELECT_BANK(x) \ ++ { \ ++ smc_outb(x, ioaddr, BANK_SELECT); \ ++ } ++ ++/* define a small delay for the reset */ ++#define SMC_DELAY() \ ++ { \ ++ smc_inb(ioaddr, RCR); \ ++ smc_inb(ioaddr, RCR); \ ++ smc_inb(ioaddr, RCR); \ ++ } ++ ++/* this enables an interrupt in the interrupt mask register */ ++#define SMC_ENABLE_INT(x) \ ++ { \ ++ byte mask; \ ++ mask = smc_inb(ioaddr, INT_MASK); \ ++ mask |= (x); \ ++ smc_outb(mask, ioaddr, INT_MASK); \ ++ } ++ ++/* this sets the absolutel interrupt mask */ ++#define SMC_SET_INT(x) \ ++ { \ ++ smc_outb((x), ioaddr, INT_MASK); \ ++ } ++ ++#endif + + /* +- . Function: smc_reset( int ioaddr ) ++ . A rather simple routine to print out a packet for debugging purposes. ++*/ ++#if SMC_DEBUG > 2 ++static void print_packet(byte * buf, int length) ++{ ++ int i; ++ int remainder; ++ int lines; ++ ++ printk("Packet of length %d \n", length); ++ lines = length / 16; ++ remainder = length % 16; ++ ++ for (i = 0; i < lines ; i ++) { ++ int cur; ++ ++ for (cur = 0; cur < 8; cur ++) { ++ byte a, b; ++ ++ a = *(buf ++); ++ b = *(buf ++); ++ printk("%02x%02x ", a, b); ++ } ++ printk("\n"); ++ } ++ for (i = 0; i < remainder/2 ; i++) { ++ byte a, b; ++ ++ a = *(buf ++); ++ b = *(buf ++); ++ printk("%02x%02x ", a, b); ++ } ++ if (remainder & 1) { ++ byte a; ++ ++ a = *buf++; ++ printk("%02x", a); ++ } ++ printk("\n"); ++} ++#else ++#define print_packet(buf,len) do { } while (0) ++#endif ++ ++/* ++ . Function: smc_reset(struct net_device *dev) + . Purpose: + . This sets the SMC91xx chip to its normal state, hopefully from whatever + . mess that any other DOS driver has put it in. +@@ -309,36 +579,37 @@ + . 5. clear all interrupts + . + */ +-static void smc_reset( int ioaddr ) ++static void smc_reset(struct net_device *dev) + { ++ u_int ioaddr = dev->base_addr; ++ + /* This resets the registers mostly to defaults, but doesn't + affect EEPROM. That seems unnecessary */ +- SMC_SELECT_BANK( 0 ); +- outw( RCR_SOFTRESET, ioaddr + RCR ); ++ SMC_SELECT_BANK(0); ++ smc_outw(RCR_SOFTRESET, ioaddr, RCR); + + /* this should pause enough for the chip to be happy */ +- SMC_DELAY( ); ++ SMC_DELAY(); + + /* Set the transmit and receive configuration registers to + default values */ +- outw( RCR_CLEAR, ioaddr + RCR ); +- outw( TCR_CLEAR, ioaddr + TCR ); ++ smc_outw(RCR_CLEAR, ioaddr, RCR); ++ smc_outw(TCR_CLEAR, ioaddr, TCR); + + /* set the control register to automatically + release successfully transmitted packets, to make the best + use out of our limited memory */ +- SMC_SELECT_BANK( 1 ); +- outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL ); ++ SMC_SELECT_BANK(1); ++ smc_outw(smc_inw(ioaddr, CONTROL) | CTL_AUTO_RELEASE, ioaddr, CONTROL); + + /* Reset the MMU */ +- SMC_SELECT_BANK( 2 ); +- outw( MC_RESET, ioaddr + MMU_CMD ); ++ SMC_SELECT_BANK(2); ++ smc_outw(MC_RESET, ioaddr, MMU_CMD); + + /* Note: It doesn't seem that waiting for the MMU busy is needed here, + but this is a place where future chipsets _COULD_ break. Be wary + of issuing another MMU command right after this */ +- +- outb( 0, ioaddr + INT_MASK ); ++ SMC_SET_INT(0); + } + + /* +@@ -349,20 +620,21 @@ + . 2. Enable the receiver + . 3. Enable interrupts + */ +-static void smc_enable( int ioaddr ) ++static void smc_enable(struct net_device *dev) + { +- SMC_SELECT_BANK( 0 ); ++ u_int ioaddr = dev->base_addr; ++ SMC_SELECT_BANK(0); + /* see the header file for options in TCR/RCR NORMAL*/ +- outw( TCR_NORMAL, ioaddr + TCR ); +- outw( RCR_NORMAL, ioaddr + RCR ); ++ smc_outw(TCR_NORMAL, ioaddr, TCR); ++ smc_outw(RCR_NORMAL, ioaddr, RCR); + + /* now, enable interrupts */ +- SMC_SELECT_BANK( 2 ); +- outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK ); ++ SMC_SELECT_BANK(2); ++ SMC_SET_INT(SMC_INTERRUPT_MASK); + } + + /* +- . Function: smc_shutdown ++ . Function: smc_shutdown(struct net_device *dev) + . Purpose: closes down the SMC91xxx chip. + . Method: + . 1. zero the interrupt mask +@@ -375,26 +647,28 @@ + . the manual says that it will wake up in response to any I/O requests + . in the register space. Empirical results do not show this working. + */ +-static void smc_shutdown( int ioaddr ) ++static void smc_shutdown(struct net_device *dev) + { ++ u_int ioaddr = dev->base_addr; ++ + /* no more interrupts for me */ +- SMC_SELECT_BANK( 2 ); +- outb( 0, ioaddr + INT_MASK ); ++ SMC_SELECT_BANK(2); ++ SMC_SET_INT(0); + + /* and tell the card to stay away from that nasty outside world */ +- SMC_SELECT_BANK( 0 ); +- outb( RCR_CLEAR, ioaddr + RCR ); +- outb( TCR_CLEAR, ioaddr + TCR ); ++ SMC_SELECT_BANK(0); ++ smc_outb(RCR_CLEAR, ioaddr, RCR); ++ smc_outb(TCR_CLEAR, ioaddr, TCR); + #if 0 + /* finally, shut the chip down */ +- SMC_SELECT_BANK( 1 ); +- outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL ); ++ SMC_SELECT_BANK(1); ++ smc_outw(smc_inw(ioaddr, CONTROL), CTL_POWERDOWN, ioaddr, CONTROL); + #endif + } + + + /* +- . Function: smc_setmulticast( int ioaddr, int count, dev_mc_list * adds ) ++ . Function: smc_setmulticast(int ioaddr, int count, dev_mc_list * adds) + . Purpose: + . This sets the internal hardware table to filter out unwanted multicast + . packets before they take up memory. +@@ -411,26 +685,28 @@ + */ + + +-static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list * addrs ) { ++static void smc_setmulticast(struct net_device *dev, int count, struct dev_mc_list * addrs) ++{ ++ u_int ioaddr = dev->base_addr; + int i; +- unsigned char multicast_table[ 8 ]; ++ unsigned char multicast_table[8]; + struct dev_mc_list * cur_addr; + /* table for flipping the order of 3 bits */ + unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + + /* start with a table of all zeros: reject all */ +- memset( multicast_table, 0, sizeof( multicast_table ) ); ++ memset(multicast_table, 0, sizeof(multicast_table)); + + cur_addr = addrs; +- for ( i = 0; i < count ; i ++, cur_addr = cur_addr->next ) { ++ for (i = 0; i < count ; i ++, cur_addr = cur_addr->next) { + int position; + + /* do we have a pointer here? */ +- if ( !cur_addr ) ++ if (!cur_addr) + break; + /* make sure this is a multicast address - shouldn't this + be a given if we have it here ? */ +- if ( !( *cur_addr->dmi_addr & 1 ) ) ++ if (!(*cur_addr->dmi_addr & 1)) + continue; + + /* only use the low order bits */ +@@ -442,15 +718,15 @@ + + } + /* now, the table can be loaded into the chipset */ +- SMC_SELECT_BANK( 3 ); ++ SMC_SELECT_BANK(3); + +- for ( i = 0; i < 8 ; i++ ) { +- outb( multicast_table[i], ioaddr + MULTICAST1 + i ); ++ for (i = 0; i < 8 ; i++) { ++ smc_outb(multicast_table[i], ioaddr, MULTICAST1 + i); + } + } + + /* +- . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * ) ++ . Function: smc_wait_to_send_packet(struct sk_buff * skb, struct net_device *) + . Purpose: + . Attempt to allocate memory for a packet, if chip-memory is not + . available, then tell the card to generate an interrupt when it +@@ -465,10 +741,10 @@ + . o (NO): Enable interrupts and let the interrupt handler deal with it. + . o (YES):Send it now. + */ +-static int smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * dev ) ++static int smc_wait_to_send_packet(struct sk_buff * skb, struct net_device * dev) + { + struct smc_local *lp = (struct smc_local *)dev->priv; +- unsigned short ioaddr = dev->base_addr; ++ u_int ioaddr = dev->base_addr; + word length; + unsigned short numPages; + word time_out; +@@ -477,15 +753,16 @@ + /* Well, I want to send the packet.. but I don't know + if I can send it right now... */ + +- if ( lp->saved_skb) { ++ if (lp->saved_skb) { + /* THIS SHOULD NEVER HAPPEN. */ + lp->stats.tx_aborted_errors++; +- printk(CARDNAME": Bad Craziness - sent packet while busy.\n" ); ++ printk("%s: Bad Craziness - sent packet while busy.\n", ++ dev->name); + return 1; + } + + length = skb->len; +- ++ + if(length < ETH_ZLEN) + { + skb = skb_padto(skb, ETH_ZLEN); +@@ -497,18 +774,18 @@ + length = ETH_ZLEN; + } + lp->saved_skb = skb; +- ++ + /* + ** The MMU wants the number of pages to be the number of 256 bytes +- ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) ++ ** 'pages', minus 1 (since a packet can't ever have 0 pages :)) + ** + ** Pkt size for allocating is data length +6 (for additional status words, + ** length and ctl!) If odd size last byte is included in this header. + */ +- numPages = ((length & 0xfffe) + 6) / 256; ++ numPages = ((length & 0xfffe) + 6) / 256; + +- if (numPages > 7 ) { +- printk(CARDNAME": Far too big packet error. \n"); ++ if (numPages > 7) { ++ printk("%s: Far too big packet error.\n", dev->name); + /* freeing the packet is a good thing here... but should + . any packets of this size get down here? */ + dev_kfree_skb (skb); +@@ -517,12 +794,13 @@ + netif_wake_queue(dev); + return 0; + } ++ + /* either way, a packet is waiting now */ + lp->packets_waiting++; + + /* now, try to allocate the memory */ +- SMC_SELECT_BANK( 2 ); +- outw( MC_ALLOC | numPages, ioaddr + MMU_CMD ); ++ SMC_SELECT_BANK(2); ++ smc_outw(MC_ALLOC | numPages, ioaddr, MMU_CMD); + /* + . Performance Hack + . +@@ -539,21 +817,21 @@ + do { + word status; + +- status = inb( ioaddr + INTERRUPT ); +- if ( status & IM_ALLOC_INT ) { ++ status = smc_inb(ioaddr, INTERRUPT); ++ if (status & IM_ALLOC_INT) { + /* acknowledge the interrupt */ +- outb( IM_ALLOC_INT, ioaddr + INTERRUPT ); +- break; ++ smc_outb(IM_ALLOC_INT, ioaddr, INTERRUPT); ++ break; + } +- } while ( -- time_out ); ++ } while (-- time_out); + +- if ( !time_out ) { ++ if (!time_out) { + /* oh well, wait until the chip finds memory later */ +- SMC_ENABLE_INT( IM_ALLOC_INT ); +- PRINTK2((CARDNAME": memory allocation deferred. \n")); ++ SMC_ENABLE_INT(IM_ALLOC_INT); ++ PRINTK2(("%s: memory allocation deferred.\n", dev->name)); + /* it's deferred, but I'll handle it later */ +- return 0; +- } ++ return 0; ++ } + /* or YES! I can send the packet now.. */ + smc_hardware_send_packet(dev); + netif_wake_queue(dev); +@@ -561,46 +839,46 @@ + } + + /* +- . Function: smc_hardware_send_packet(struct net_device * ) ++ . Function: smc_hardware_send_packet(struct net_device *) + . Purpose: + . This sends the actual packet to the SMC9xxx chip. + . + . Algorithm: + . First, see if a saved_skb is available. +- . ( this should NOT be called if there is no 'saved_skb' ++ . (this should NOT be called if there is no 'saved_skb' + . Now, find the packet number that the chip allocated + . Point the data pointers at it in memory + . Set the length word in the chip's memory + . Dump the packet to chip memory +- . Check if a last byte is needed ( odd length packet ) ++ . Check if a last byte is needed (odd length packet) + . if so, set the control flag right + . Tell the card to send it + . Enable the transmit interrupt, so I know if it failed + . Free the kernel data if I actually sent it. + */ +-static void smc_hardware_send_packet( struct net_device * dev ) ++static void smc_hardware_send_packet(struct net_device *dev) + { + struct smc_local *lp = (struct smc_local *)dev->priv; +- byte packet_no; +- struct sk_buff * skb = lp->saved_skb; +- word length; +- unsigned short ioaddr; +- byte * buf; +- +- ioaddr = dev->base_addr; ++ struct sk_buff *skb = lp->saved_skb; ++ word length, lastword; ++ u_int ioaddr = dev->base_addr; ++ byte packet_no; ++ byte *buf; + +- if ( !skb ) { +- PRINTK((CARDNAME": In XMIT with no packet to send \n")); ++ if (!skb) { ++ PRINTK(("%s: In XMIT with no packet to send\n", dev->name)); + return; + } ++ + length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + buf = skb->data; + + /* If I get here, I _know_ there is a packet slot waiting for me */ +- packet_no = inb( ioaddr + PNR_ARR + 1 ); +- if ( packet_no & 0x80 ) { ++ packet_no = smc_inb(ioaddr, PNR_ARR + 1); ++ if (packet_no & 0x80) { + /* or isn't there? BAD CHIP! */ +- printk(KERN_DEBUG CARDNAME": Memory allocation failed. \n"); ++ printk(KERN_DEBUG "%s: Memory allocation failed.\n", ++ dev->name); + dev_kfree_skb_any(skb); + lp->saved_skb = NULL; + netif_wake_queue(dev); +@@ -608,26 +886,19 @@ + } + + /* we have a packet address, so tell the card to use it */ +- outb( packet_no, ioaddr + PNR_ARR ); ++ smc_outb(packet_no, ioaddr, PNR_ARR); + + /* point to the beginning of the packet */ +- outw( PTR_AUTOINC , ioaddr + POINTER ); ++ smc_outw(PTR_AUTOINC, ioaddr, POINTER); + +- PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length )); +-#if SMC_DEBUG > 2 +- print_packet( buf, length ); +-#endif ++ PRINTK3(("%s: Trying to xmit packet of length %x\n", ++ dev->name, length)); + +- /* send the packet length ( +6 for status, length and ctl byte ) +- and the status word ( set to zeros ) */ +-#ifdef USE_32_BIT +- outl( (length +6 ) << 16 , ioaddr + DATA_1 ); +-#else +- outw( 0, ioaddr + DATA_1 ); +- /* send the packet length ( +6 for status words, length, and ctl*/ +- outb( (length+6) & 0xFF,ioaddr + DATA_1 ); +- outb( (length+6) >> 8 , ioaddr + DATA_1 ); +-#endif ++ print_packet(buf, length); ++ ++ /* send the packet length (+6 for status, length and ctl byte) ++ and the status word (set to zeros) */ ++ smc_outl((length + 6) << 16, ioaddr, DATA_1); + + /* send the actual data + . I _think_ it's faster to send the longs first, and then +@@ -636,32 +907,22 @@ + . a good idea to check which is optimal? But that could take + . almost as much time as is saved? + */ +-#ifdef USE_32_BIT +- if ( length & 0x2 ) { +- outsl(ioaddr + DATA_1, buf, length >> 2 ); +- outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1); +- } +- else +- outsl(ioaddr + DATA_1, buf, length >> 2 ); +-#else +- outsw(ioaddr + DATA_1 , buf, (length ) >> 1); +-#endif +- /* Send the last byte, if there is one. */ ++ smc_outs(ioaddr, DATA_1, buf, length); + +- if ( (length & 1) == 0 ) { +- outw( 0, ioaddr + DATA_1 ); +- } else { +- outb( buf[length -1 ], ioaddr + DATA_1 ); +- outb( 0x20, ioaddr + DATA_1); +- } ++ /* Send the last byte, if there is one. */ ++ if ((length & 1) == 0) ++ lastword = 0; ++ else ++ lastword = 0x2000 | buf[length - 1]; ++ smc_outw(lastword, ioaddr, DATA_1); + + /* enable the interrupts */ +- SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) ); ++ SMC_ENABLE_INT(IM_TX_INT | IM_TX_EMPTY_INT); + + /* and let the chipset deal with it */ +- outw( MC_ENQUEUE , ioaddr + MMU_CMD ); ++ smc_outw(MC_ENQUEUE, ioaddr, MMU_CMD); + +- PRINTK2((CARDNAME": Sent packet of length %d \n",length)); ++ PRINTK2(("%s: Sent packet of length %d\n", dev->name, length)); + + lp->saved_skb = NULL; + dev_kfree_skb_any (skb); +@@ -676,7 +937,7 @@ + + /*------------------------------------------------------------------------- + | +- | smc_init( struct net_device * dev ) ++ | smc_init(struct net_device * dev) + | Input parameters: + | dev->base_addr == 0, try to find all possible locations + | dev->base_addr == 1, return failure code +@@ -691,6 +952,65 @@ + */ + int __init smc_init(struct net_device *dev) + { ++ int ret = -ENODEV; ++#if defined(CONFIG_ASSABET_NEPONSET) ++ if (machine_is_assabet() && machine_has_neponset()) { ++ unsigned int *addr; ++ unsigned char ecor; ++ unsigned long flags; ++ ++ NCR_0 |= NCR_ENET_OSC_EN; ++ dev->irq = IRQ_NEPONSET_SMC9196; ++ ++ /* ++ * Map the attribute space. This is overkill, but clean. ++ */ ++ addr = ioremap(0x18000000 + (1 << 25), 64 * 1024 * 4); ++ if (!addr) ++ return -ENOMEM; ++ ++ /* ++ * Reset the device. We must disable IRQs around this. ++ */ ++ local_irq_save(flags); ++ ecor = readl(addr + ECOR) & ~ECOR_RESET; ++ writel(ecor | ECOR_RESET, addr + ECOR); ++ udelay(100); ++ ++ /* ++ * The device will ignore all writes to the enable bit while ++ * reset is asserted, even if the reset bit is cleared in the ++ * same write. Must clear reset first, then enable the device. ++ */ ++ writel(ecor, addr + ECOR); ++ writel(ecor | ECOR_ENABLE, addr + ECOR); ++ ++ /* ++ * Force byte mode. ++ */ ++ writel(readl(addr + ECSR) | ECSR_IOIS8, addr + ECSR); ++ local_irq_restore(flags); ++ ++ iounmap(addr); ++ ++ /* ++ * Wait for the chip to wake up. ++ */ ++ mdelay(1); ++ ++ /* ++ * Map the real registers. ++ */ ++ addr = ioremap(0x18000000, 8 * 1024); ++ if (!addr) ++ return -ENOMEM; ++ ++ ret = smc_probe(dev, (int)addr); ++ if (ret) ++ iounmap(addr); ++ } ++ ++#elif defined(CONFIG_ISA) + int i; + int base_addr = dev->base_addr; + +@@ -708,7 +1028,8 @@ + return 0; + + /* couldn't find anything */ +- return -ENODEV; ++#endif ++ return ret; + } + + /*---------------------------------------------------------------------- +@@ -718,10 +1039,11 @@ + . interrupt, so an auto-detect routine can detect it, and find the IRQ, + ------------------------------------------------------------------------ + */ +-int __init smc_findirq( int ioaddr ) ++int __init smc_findirq(struct net_device *dev) + { + int timeout = 20; + unsigned long cookie; ++ u_int ioaddr = dev->base_addr; + + + /* I have to do a STI() here, because this is called from +@@ -737,26 +1059,25 @@ + * when done. + */ + +- ++ /* enable ALLOCation interrupts ONLY. */ + SMC_SELECT_BANK(2); +- /* enable ALLOCation interrupts ONLY */ +- outb( IM_ALLOC_INT, ioaddr + INT_MASK ); ++ SMC_SET_INT(IM_ALLOC_INT); + + /* + . Allocate 512 bytes of memory. Note that the chip was just + . reset so all the memory is available + */ +- outw( MC_ALLOC | 1, ioaddr + MMU_CMD ); ++ smc_outw(MC_ALLOC | 1, ioaddr, MMU_CMD); + + /* + . Wait until positive that the interrupt has been generated + */ +- while ( timeout ) { ++ while (timeout) { + byte int_status; + +- int_status = inb( ioaddr + INTERRUPT ); ++ int_status = smc_inb(ioaddr, INTERRUPT); + +- if ( int_status & IM_ALLOC_INT ) ++ if (int_status & IM_ALLOC_INT) + break; /* got the interrupt */ + timeout--; + } +@@ -775,7 +1096,7 @@ + SMC_DELAY(); + + /* and disable all interrupts again */ +- outb( 0, ioaddr + INT_MASK ); ++ SMC_SET_INT(0); + + /* clear hardware interrupts again, because that's how it + was when I was called... */ +@@ -785,8 +1106,87 @@ + return probe_irq_off(cookie); + } + ++static int __init smc_probe_chip(struct net_device *dev, int ioaddr) ++{ ++ unsigned int temp; ++ ++ /* First, see if the high byte is 0x33 */ ++ temp = smc_inw(ioaddr, BANK_SELECT); ++ if ((temp & 0xFF00) != 0x3300) ++ return -ENODEV; ++ ++ /* The above MIGHT indicate a device, but I need to write to further ++ test this. */ ++ smc_outw(0, ioaddr, BANK_SELECT); ++ temp = smc_inw(ioaddr, BANK_SELECT); ++ if ((temp & 0xFF00) != 0x3300) ++ return -ENODEV; ++ ++#ifndef CONFIG_ASSABET_NEPONSET ++ /* well, we've already written once, so hopefully another time won't ++ hurt. This time, I need to switch the bank register to bank 1, ++ so I can access the base address register */ ++ SMC_SELECT_BANK(1); ++ temp = smc_inw(ioaddr, BASE); ++ if (ioaddr != (temp >> 3 & 0x3E0)) { ++ printk("%s: IOADDR %x doesn't match configuration (%x)." ++ "Probably not a SMC chip\n", dev->name, ++ ioaddr, (base_address_register >> 3) & 0x3E0); ++ /* well, the base address register didn't match. Must not have ++ been a SMC chip after all. */ ++ return -ENODEV; ++ } ++#endif ++ ++ return 0; ++} ++ ++/* ++ . If dev->irq is 0, then the device has to be banged on to see ++ . what the IRQ is. ++ . ++ . This banging doesn't always detect the IRQ, for unknown reasons. ++ . a workaround is to reset the chip and try again. ++ . ++ . Interestingly, the DOS packet driver *SETS* the IRQ on the card to ++ . be what is requested on the command line. I don't do that, mostly ++ . because the card that I have uses a non-standard method of accessing ++ . the IRQs, and because this _should_ work in most configurations. ++ . ++ . Specifying an IRQ is done with the assumption that the user knows ++ . what (s)he is doing. No checking is done!!!! ++ . ++*/ ++static int __init smc_probe_irq(struct net_device *dev) ++{ ++ if (dev->irq < 2) { ++ int trials; ++ ++ trials = 3; ++ while (trials--) { ++ dev->irq = smc_findirq(dev); ++ if (dev->irq) ++ break; ++ /* kick the card and try again */ ++ smc_reset(dev); ++ } ++ } ++ if (dev->irq == 0) { ++ printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n", ++ dev->name); ++ return -ENODEV; ++ } ++ ++ /* ++ * Some machines (eg, PCs) need to cannonicalize their IRQs. ++ */ ++ dev->irq = irq_cannonicalize(dev->irq); ++ ++ return 0; ++} ++ + /*---------------------------------------------------------------------- +- . Function: smc_probe( int ioaddr ) ++ . Function: smc_probe(struct net_device *dev, int ioaddr) + . + . Purpose: + . Tests to see if a given ioaddr points to an SMC9xxx chip. +@@ -816,16 +1216,14 @@ + */ + static int __init smc_probe(struct net_device *dev, int ioaddr) + { ++ struct smc_local *smc; + int i, memory, retval; + static unsigned version_printed; +- unsigned int bank; + + const char *version_string; +- const char *if_string; + + /* registers */ + word revision_register; +- word base_address_register; + word configuration_register; + word memory_info_register; + word memory_cfg_register; +@@ -834,44 +1232,24 @@ + if (!request_region(ioaddr, SMC_IO_EXTENT, dev->name)) + return -EBUSY; + +- /* First, see if the high byte is 0x33 */ +- bank = inw( ioaddr + BANK_SELECT ); +- if ( (bank & 0xFF00) != 0x3300 ) { +- retval = -ENODEV; +- goto err_out; +- } +- /* The above MIGHT indicate a device, but I need to write to further +- test this. */ +- outw( 0x0, ioaddr + BANK_SELECT ); +- bank = inw( ioaddr + BANK_SELECT ); +- if ( (bank & 0xFF00 ) != 0x3300 ) { +- retval = -ENODEV; +- goto err_out; +- } +- /* well, we've already written once, so hopefully another time won't +- hurt. This time, I need to switch the bank register to bank 1, +- so I can access the base address register */ +- SMC_SELECT_BANK(1); +- base_address_register = inw( ioaddr + BASE ); +- if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) ) { +- printk(CARDNAME ": IOADDR %x doesn't match configuration (%x)." +- "Probably not a SMC chip\n", +- ioaddr, base_address_register >> 3 & 0x3E0 ); +- /* well, the base address register didn't match. Must not have +- been a SMC chip after all. */ +- retval = -ENODEV; ++ /* ++ * Do the basic probes. ++ */ ++ retval = smc_probe_chip(dev, ioaddr); ++ if (retval) + goto err_out; +- } + + /* check if the revision register is something that I recognize. + These might need to be added to later, as future revisions + could be added. */ + SMC_SELECT_BANK(3); +- revision_register = inw( ioaddr + REVISION ); +- if ( !chip_ids[ ( revision_register >> 4 ) & 0xF ] ) { ++ revision_register = smc_inw(ioaddr, REVISION); ++ version_string = chip_ids[(revision_register >> 4) & 15]; ++ if (!version_string) { + /* I don't recognize this chip, so... */ +- printk(CARDNAME ": IO %x: Unrecognized revision register:" +- " %x, Contact author. \n", ioaddr, revision_register ); ++ printk("%s: IO %x: unrecognized revision register: %x, " ++ "contact author.\n", dev->name, ioaddr, ++ revision_register); + + retval = -ENODEV; + goto err_out; +@@ -882,138 +1260,122 @@ + against the hardware address, or do some other tests. */ + + if (version_printed++ == 0) +- printk("%s", version); ++ printk(KERN_INFO "%s", version); + + /* fill in some of the fields */ + dev->base_addr = ioaddr; + + /* +- . Get the MAC address ( bank 1, regs 4 - 9 ) ++ . Get the MAC address (bank 1, regs 4 - 9) + */ +- SMC_SELECT_BANK( 1 ); +- for ( i = 0; i < 6; i += 2 ) { ++ SMC_SELECT_BANK(1); ++ for (i = 0; i < 6; i += 2) { + word address; + +- address = inw( ioaddr + ADDR0 + i ); +- dev->dev_addr[ i + 1] = address >> 8; +- dev->dev_addr[ i ] = address & 0xFF; ++ address = smc_inw(ioaddr, ADDR0 + i); ++ dev->dev_addr[i + 1] = address >> 8; ++ dev->dev_addr[i] = address & 0xFF; + } + ++ if (!is_valid_ether_addr(dev->dev_addr)) ++ printk("%s: Invalid ethernet MAC address. Please set using " ++ "ifconfig\n", dev->name); ++ + /* get the memory information */ + +- SMC_SELECT_BANK( 0 ); +- memory_info_register = inw( ioaddr + MIR ); +- memory_cfg_register = inw( ioaddr + MCR ); +- memory = ( memory_cfg_register >> 9 ) & 0x7; /* multiplier */ +- memory *= 256 * ( memory_info_register & 0xFF ); ++ SMC_SELECT_BANK(0); ++ memory_info_register = smc_inw(ioaddr, MIR); ++ memory_cfg_register = smc_inw(ioaddr, MCR); ++ memory = (memory_cfg_register >> 9) & 0x7; /* multiplier */ ++ memory *= 256 * (memory_info_register & 0xFF); ++ ++ /* now, reset the chip, and put it into a known state */ ++ smc_reset(dev); + + /* +- Now, I want to find out more about the chip. This is sort of +- redundant, but it's cleaner to have it in both, rather than having +- one VERY long probe procedure. +- */ +- SMC_SELECT_BANK(3); +- revision_register = inw( ioaddr + REVISION ); +- version_string = chip_ids[ ( revision_register >> 4 ) & 0xF ]; +- if ( !version_string ) { +- /* I shouldn't get here because this call was done before.... */ +- retval = -ENODEV; ++ * Ok, now that we have everything in a ++ * sane state, probe for the interrupt. ++ */ ++ retval = smc_probe_irq(dev); ++ if (retval) + goto err_out; +- } + +- /* is it using AUI or 10BaseT ? */ +- if ( dev->if_port == 0 ) { +- SMC_SELECT_BANK(1); +- configuration_register = inw( ioaddr + CONFIG ); +- if ( configuration_register & CFG_AUI_SELECT ) +- dev->if_port = 2; +- else +- dev->if_port = 1; ++ /* Initialize the private structure. */ ++ if (dev->priv == NULL) { ++ dev->priv = kmalloc(sizeof(struct smc_local), GFP_KERNEL); ++ if (dev->priv == NULL) { ++ retval = -ENOMEM; ++ goto err_out; ++ } + } +- if_string = interfaces[ dev->if_port - 1 ]; + +- /* now, reset the chip, and put it into a known state */ +- smc_reset( ioaddr ); ++ smc = dev->priv; ++ ++ /* set the private data to zero by default */ ++ memset(smc, 0, sizeof(struct smc_local)); + + /* +- . If dev->irq is 0, then the device has to be banged on to see +- . what the IRQ is. +- . +- . This banging doesn't always detect the IRQ, for unknown reasons. +- . a workaround is to reset the chip and try again. +- . +- . Interestingly, the DOS packet driver *SETS* the IRQ on the card to +- . be what is requested on the command line. I don't do that, mostly +- . because the card that I have uses a non-standard method of accessing +- . the IRQs, and because this _should_ work in most configurations. +- . +- . Specifying an IRQ is done with the assumption that the user knows +- . what (s)he is doing. No checking is done!!!! +- . +- */ +- if ( dev->irq < 2 ) { +- int trials; ++ * Get the interface characteristics. ++ * is it using AUI or 10BaseT ? ++ */ ++ switch (dev->if_port) { ++ case IF_PORT_10BASET: ++ smc->port = PORT_TP; ++ break; ++ ++ case IF_PORT_AUI: ++ smc->port = PORT_AUI; ++ break; + +- trials = 3; +- while ( trials-- ) { +- dev->irq = smc_findirq( ioaddr ); +- if ( dev->irq ) +- break; +- /* kick the card and try again */ +- smc_reset( ioaddr ); ++ default: ++ SMC_SELECT_BANK(1); ++ configuration_register = smc_inw(ioaddr, CONFIG); ++ if (configuration_register & CFG_AUI_SELECT) { ++ dev->if_port = IF_PORT_AUI; ++ smc->port = PORT_AUI; ++ } else { ++ dev->if_port = IF_PORT_10BASET; ++ smc->port = PORT_TP; + } +- } +- if (dev->irq == 0 ) { +- printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n"); +- retval = -ENODEV; +- goto err_out; ++ break; + } + +- /* now, print out the card info, in a short format.. */ ++ /* all interfaces are half-duplex by default */ ++ smc->duplex = DUPLEX_HALF; + +- printk("%s: %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name, +- version_string, revision_register & 0xF, ioaddr, dev->irq, +- if_string, memory ); ++ /* now, print out the card info, in a short format.. */ ++ printk("%s: %s (rev %d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name, ++ version_string, revision_register & 15, ioaddr, dev->irq, ++ interfaces[smc->port], memory); + /* + . Print the Ethernet address + */ + printk("ADDR: "); + for (i = 0; i < 5; i++) +- printk("%2.2x:", dev->dev_addr[i] ); +- printk("%2.2x \n", dev->dev_addr[5] ); +- +- +- /* Initialize the private structure. */ +- if (dev->priv == NULL) { +- dev->priv = kmalloc(sizeof(struct smc_local), GFP_KERNEL); +- if (dev->priv == NULL) { +- retval = -ENOMEM; +- goto err_out; +- } +- } +- /* set the private data to zero by default */ +- memset(dev->priv, 0, sizeof(struct smc_local)); ++ printk("%2.2x:", dev->dev_addr[i]); ++ printk("%2.2x\n", dev->dev_addr[5]); + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + + /* Grab the IRQ */ +- retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev); +- if (retval) { ++ retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev); ++ if (retval) { + printk("%s: unable to get IRQ %d (irqval=%d).\n", dev->name, + dev->irq, retval); + kfree(dev->priv); + dev->priv = NULL; +- goto err_out; +- } ++ goto err_out; ++ } + +- dev->open = smc_open; +- dev->stop = smc_close; +- dev->hard_start_xmit = smc_wait_to_send_packet; +- dev->tx_timeout = smc_timeout; +- dev->watchdog_timeo = HZ/20; +- dev->get_stats = smc_query_statistics; +- dev->set_multicast_list = smc_set_multicast_list; ++ dev->open = smc_open; ++ dev->stop = smc_close; ++ dev->hard_start_xmit = smc_wait_to_send_packet; ++ dev->tx_timeout = smc_timeout; ++ dev->watchdog_timeo = HZ/20; ++ dev->get_stats = smc_query_statistics; ++ dev->set_multicast_list = smc_set_multicast_list; ++ dev->do_ioctl = smc_ioctl; + + return 0; + +@@ -1022,42 +1384,43 @@ + return retval; + } + +-#if SMC_DEBUG > 2 +-static void print_packet( byte * buf, int length ) ++/* ++ * This is responsible for setting the chip appropriately ++ * for the interface type. This should only be called while ++ * the interface is up and running. ++ */ ++static void smc_set_port(struct net_device *dev) + { +-#if 0 +- int i; +- int remainder; +- int lines; +- +- printk("Packet of length %d \n", length ); +- lines = length / 16; +- remainder = length % 16; ++ struct smc_local *smc = dev->priv; ++ u_int ioaddr = dev->base_addr; ++ u_int val; + +- for ( i = 0; i < lines ; i ++ ) { +- int cur; +- +- for ( cur = 0; cur < 8; cur ++ ) { +- byte a, b; +- +- a = *(buf ++ ); +- b = *(buf ++ ); +- printk("%02x%02x ", a, b ); +- } +- printk("\n"); ++ SMC_SELECT_BANK(1); ++ val = smc_inw(ioaddr, CONFIG); ++ switch (smc->port) { ++ case PORT_TP: ++ val &= ~CFG_AUI_SELECT; ++ break; ++ ++ case PORT_AUI: ++ val |= CFG_AUI_SELECT; ++ break; + } +- for ( i = 0; i < remainder/2 ; i++ ) { +- byte a, b; ++ smc_outw(val, ioaddr, CONFIG); + +- a = *(buf ++ ); +- b = *(buf ++ ); +- printk("%02x%02x ", a, b ); ++ SMC_SELECT_BANK(0); ++ val = smc_inw(ioaddr, TCR); ++ switch (smc->duplex) { ++ case DUPLEX_HALF: ++ val &= ~TCR_FDSE; ++ break; ++ ++ case DUPLEX_FULL: ++ val |= TCR_FDSE; ++ break; + } +- printk("\n"); +-#endif ++ smc_outw(val, ioaddr, TCR); + } +-#endif +- + + /* + * Open and Initialize the board +@@ -1067,48 +1430,141 @@ + */ + static int smc_open(struct net_device *dev) + { +- int ioaddr = dev->base_addr; ++ struct smc_local *smc = dev->priv; ++ u_int ioaddr = dev->base_addr; ++ int i; + +- int i; /* used to set hw ethernet address */ ++ /* ++ * Check that the address is valid. If its not, refuse ++ * to bring the device up. The user must specify an ++ * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx ++ */ ++ if (!is_valid_ether_addr(dev->dev_addr)) ++ return -EINVAL; + + /* clear out all the junk that was put here before... */ +- memset(dev->priv, 0, sizeof(struct smc_local)); ++ smc->saved_skb = NULL; ++ smc->packets_waiting = 0; + + /* reset the hardware */ +- +- smc_reset( ioaddr ); +- smc_enable( ioaddr ); ++ smc_reset(dev); ++ smc_enable(dev); + + /* Select which interface to use */ +- +- SMC_SELECT_BANK( 1 ); +- if ( dev->if_port == 1 ) { +- outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT, +- ioaddr + CONFIG ); +- } +- else if ( dev->if_port == 2 ) { +- outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT, +- ioaddr + CONFIG ); +- } ++ smc_set_port(dev); + + /* +- According to Becker, I have to set the hardware address ++ According to Becker, I have to set the hardware address + at this point, because the (l)user can set it with an + ioctl. Easily done... + */ +- SMC_SELECT_BANK( 1 ); +- for ( i = 0; i < 6; i += 2 ) { ++ SMC_SELECT_BANK(1); ++ for (i = 0; i < 6; i += 2) { + word address; + +- address = dev->dev_addr[ i + 1 ] << 8 ; +- address |= dev->dev_addr[ i ]; +- outw( address, ioaddr + ADDR0 + i ); ++ address = dev->dev_addr[i + 1] << 8 ; ++ address |= dev->dev_addr[i]; ++ smc_outw(address, ioaddr, ADDR0 + i); + } + + netif_start_queue(dev); + return 0; + } + ++/* ++ * This is our template. Fill the rest in at run-time ++ */ ++static const struct ethtool_cmd ecmd_template = { ++ supported: SUPPORTED_10baseT_Half | ++ SUPPORTED_10baseT_Full | ++ SUPPORTED_TP | ++ SUPPORTED_AUI, ++ speed: SPEED_10, ++ autoneg: AUTONEG_DISABLE, ++ maxtxpkt: 1, ++ maxrxpkt: 1, ++ transceiver: XCVR_INTERNAL, ++}; ++ ++static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++ struct smc_local *smc = dev->priv; ++ u32 etcmd; ++ int ret = -EINVAL; ++ ++ if (cmd != SIOCETHTOOL) ++ return -EOPNOTSUPP; ++ ++ if (get_user(etcmd, (u32 *)rq->ifr_data)) ++ return -EFAULT; ++ ++ switch (etcmd) { ++ case ETHTOOL_GSET: { ++ struct ethtool_cmd ecmd = ecmd_template; ++ ++ ecmd.cmd = etcmd; ++ ecmd.port = smc->port; ++ ecmd.duplex = smc->duplex; ++ ++ ret = copy_to_user(rq->ifr_data, &ecmd, sizeof(ecmd)) ++ ? -EFAULT : 0; ++ break; ++ } ++ ++ case ETHTOOL_SSET: { ++ struct ethtool_cmd ecmd; ++ ++ ret = -EPERM; ++ if (!capable(CAP_NET_ADMIN)) ++ break; ++ ++ ret = -EFAULT; ++ if (copy_from_user(&ecmd, rq->ifr_data, sizeof(ecmd))) ++ break; ++ ++ /* ++ * Sanity-check the arguments. ++ */ ++ ret = -EINVAL; ++ if (ecmd.autoneg != AUTONEG_DISABLE) ++ break; ++ if (ecmd.speed != SPEED_10) ++ break; ++ if (ecmd.duplex != DUPLEX_HALF && ecmd.duplex != DUPLEX_FULL) ++ break; ++ if (ecmd.port != PORT_TP && ecmd.port != PORT_AUI) ++ break; ++ ++ smc->port = ecmd.port; ++ smc->duplex = ecmd.duplex; ++ ++ if (netif_running(dev)) ++ smc_set_port(dev); ++ ++ ret = 0; ++ break; ++ } ++ ++ case ETHTOOL_GDRVINFO: { ++ struct ethtool_drvinfo edrv; ++ ++ memset(&edrv, 0, sizeof(edrv)); ++ ++ edrv.cmd = etcmd; ++ strcpy(edrv.driver, DRV_NAME); ++ strcpy(edrv.version, DRV_VERSION); ++ sprintf(edrv.bus_info, "ISA:%8.8lx:%d", ++ dev->base_addr, dev->irq); ++ ++ ret = copy_to_user(rq->ifr_data, &edrv, sizeof(edrv)) ++ ? -EFAULT : 0; ++ break; ++ } ++ } ++ ++ return ret; ++} ++ + /*-------------------------------------------------------- + . Called by the kernel to send a packet out into the void + . of the net. This routine is largely based on +@@ -1120,12 +1576,10 @@ + { + /* If we get here, some higher level has decided we are broken. + There should really be a "kick me" function call instead. */ +- printk(KERN_WARNING CARDNAME": transmit timed out, %s?\n", +- tx_done(dev) ? "IRQ conflict" : +- "network cable problem"); ++ printk(KERN_WARNING "%s: transmit timed out\n", dev->name); + /* "kick" the adaptor */ +- smc_reset( dev->base_addr ); +- smc_enable( dev->base_addr ); ++ smc_reset(dev); ++ smc_enable(dev); + dev->trans_start = jiffies; + /* clear anything saved */ + ((struct smc_local *)dev->priv)->saved_skb = NULL; +@@ -1145,10 +1599,10 @@ + . + ---------------------------------------------------------------------*/ + +-static void smc_interrupt(int irq, void * dev_id, struct pt_regs * regs) ++static void smc_interrupt(int irq, void * dev_id, struct pt_regs * regs) + { + struct net_device *dev = dev_id; +- int ioaddr = dev->base_addr; ++ u_int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + + byte status; +@@ -1161,45 +1615,45 @@ + + + +- PRINTK3((CARDNAME": SMC interrupt started \n")); ++ PRINTK3(("%s: SMC interrupt started\n", dev->name)); + +- saved_bank = inw( ioaddr + BANK_SELECT ); ++ saved_bank = smc_inw(ioaddr, BANK_SELECT); + + SMC_SELECT_BANK(2); +- saved_pointer = inw( ioaddr + POINTER ); ++ saved_pointer = smc_inw(ioaddr, POINTER); + +- mask = inb( ioaddr + INT_MASK ); ++ mask = smc_inb(ioaddr, INT_MASK); + /* clear all interrupts */ +- outb( 0, ioaddr + INT_MASK ); ++ SMC_SET_INT(0); + + + /* set a timeout value, so I don't stay here forever */ + timeout = 4; + +- PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x \n", mask )); ++ PRINTK2((KERN_WARNING "%s: MASK IS %x\n", dev->name, mask)); + do { + /* read the status flag, and mask it */ +- status = inb( ioaddr + INTERRUPT ) & mask; +- if (!status ) ++ status = smc_inb(ioaddr, INTERRUPT) & mask; ++ if (!status) + break; + +- PRINTK3((KERN_WARNING CARDNAME +- ": Handling interrupt status %x \n", status )); ++ PRINTK3((KERN_WARNING "%s: handling interrupt status %x\n", ++ dev->name, status)); + + if (status & IM_RCV_INT) { + /* Got a packet(s). */ +- PRINTK2((KERN_WARNING CARDNAME +- ": Receive Interrupt\n")); ++ PRINTK2((KERN_WARNING "%s: receive interrupt\n", ++ dev->name)); + smc_rcv(dev); +- } else if (status & IM_TX_INT ) { +- PRINTK2((KERN_WARNING CARDNAME +- ": TX ERROR handled\n")); ++ } else if (status & IM_TX_INT) { ++ PRINTK2((KERN_WARNING "%s: TX ERROR handled\n", ++ dev->name)); + smc_tx(dev); +- outb(IM_TX_INT, ioaddr + INTERRUPT ); +- } else if (status & IM_TX_EMPTY_INT ) { ++ smc_outb(IM_TX_INT, ioaddr, INTERRUPT); ++ } else if (status & IM_TX_EMPTY_INT) { + /* update stats */ +- SMC_SELECT_BANK( 0 ); +- card_stats = inw( ioaddr + COUNTER ); ++ SMC_SELECT_BANK(0); ++ card_stats = smc_inw(ioaddr, COUNTER); + /* single collisions */ + lp->stats.collisions += card_stats & 0xF; + card_stats >>= 4; +@@ -1208,60 +1662,63 @@ + + /* these are for when linux supports these statistics */ + +- SMC_SELECT_BANK( 2 ); +- PRINTK2((KERN_WARNING CARDNAME +- ": TX_BUFFER_EMPTY handled\n")); +- outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT ); ++ SMC_SELECT_BANK(2); ++ PRINTK2((KERN_WARNING "%s: TX_BUFFER_EMPTY handled\n", ++ dev->name)); ++ smc_outb(IM_TX_EMPTY_INT, ioaddr, INTERRUPT); + mask &= ~IM_TX_EMPTY_INT; + lp->stats.tx_packets += lp->packets_waiting; + lp->packets_waiting = 0; + +- } else if (status & IM_ALLOC_INT ) { +- PRINTK2((KERN_DEBUG CARDNAME +- ": Allocation interrupt \n")); ++ } else if (status & IM_ALLOC_INT) { ++ PRINTK2((KERN_DEBUG "%s: Allocation interrupt\n", ++ dev->name)); + /* clear this interrupt so it doesn't happen again */ + mask &= ~IM_ALLOC_INT; + +- smc_hardware_send_packet( dev ); ++ smc_hardware_send_packet(dev); + + /* enable xmit interrupts based on this */ +- mask |= ( IM_TX_EMPTY_INT | IM_TX_INT ); ++ mask |= (IM_TX_EMPTY_INT | IM_TX_INT); + + /* and let the card send more packets to me */ + netif_wake_queue(dev); + +- PRINTK2((CARDNAME": Handoff done successfully.\n")); +- } else if (status & IM_RX_OVRN_INT ) { ++ PRINTK2(("%s: Handoff done successfully.\n", ++ dev->name)); ++ } else if (status & IM_RX_OVRN_INT) { + lp->stats.rx_errors++; + lp->stats.rx_fifo_errors++; +- outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT ); +- } else if (status & IM_EPH_INT ) { +- PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT \n")); +- } else if (status & IM_ERCV_INT ) { +- PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT \n")); +- outb( IM_ERCV_INT, ioaddr + INTERRUPT ); ++ smc_outb(IM_RX_OVRN_INT, ioaddr, INTERRUPT); ++ } else if (status & IM_EPH_INT) { ++ PRINTK(("%s: UNSUPPORTED: EPH INTERRUPT\n", ++ dev->name)); ++ } else if (status & IM_ERCV_INT) { ++ PRINTK(("%s: UNSUPPORTED: ERCV INTERRUPT\n", ++ dev->name)); ++ smc_outb(IM_ERCV_INT, ioaddr, INTERRUPT); + } +- } while ( timeout -- ); ++ } while (timeout --); + + + /* restore state register */ +- SMC_SELECT_BANK( 2 ); +- outb( mask, ioaddr + INT_MASK ); ++ SMC_SELECT_BANK(2); ++ SMC_SET_INT(mask); + +- PRINTK3(( KERN_WARNING CARDNAME ": MASK is now %x \n", mask )); +- outw( saved_pointer, ioaddr + POINTER ); ++ PRINTK3((KERN_WARNING "%s: MASK is now %x\n", dev->name, mask)); ++ smc_outw(saved_pointer, ioaddr, POINTER); + +- SMC_SELECT_BANK( saved_bank ); ++ SMC_SELECT_BANK(saved_bank); + +- PRINTK3((CARDNAME ": Interrupt done\n")); ++ PRINTK3(("%s: Interrupt done\n", dev->name)); + return; + } + + /*------------------------------------------------------------- + . +- . smc_rcv - receive a packet from the card ++ . smc_rcv - receive a packet from the card + . +- . There is ( at least ) a packet waiting to be read from ++ . There is (at least) a packet waiting to be read from + . chip-memory. + . + . o Read the status +@@ -1272,55 +1729,57 @@ + static void smc_rcv(struct net_device *dev) + { + struct smc_local *lp = (struct smc_local *)dev->priv; +- int ioaddr = dev->base_addr; ++ u_int ioaddr = dev->base_addr; + int packet_number; + word status; + word packet_length; + + /* assume bank 2 */ + +- packet_number = inw( ioaddr + FIFO_PORTS ); ++ packet_number = smc_inw(ioaddr, FIFO_PORTS); + +- if ( packet_number & FP_RXEMPTY ) { ++ if (packet_number & FP_RXEMPTY) { + /* we got called , but nothing was on the FIFO */ +- PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO. \n")); ++ PRINTK(("%s: WARNING: smc_rcv with nothing on FIFO.\n", ++ dev->name)); + /* don't need to restore anything */ + return; + } + + /* start reading from the start of the packet */ +- outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER ); ++ smc_outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr, POINTER); + + /* First two words are status and packet_length */ +- status = inw( ioaddr + DATA_1 ); +- packet_length = inw( ioaddr + DATA_1 ); ++ status = smc_inw(ioaddr, DATA_1); ++ packet_length = smc_inw(ioaddr, DATA_1); + + packet_length &= 0x07ff; /* mask off top bits */ + +- PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length )); ++ PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length)); + /* + . the packet length contains 3 extra words : + . status, length, and an extra word with an odd byte . + */ + packet_length -= 6; + +- if ( !(status & RS_ERRORS ) ){ ++ if (!(status & RS_ERRORS)){ + /* do stuff to make a new packet */ + struct sk_buff * skb; + byte * data; + + /* read one extra byte */ +- if ( status & RS_ODDFRAME ) ++ if (status & RS_ODDFRAME) + packet_length++; + + /* set multicast stats */ +- if ( status & RS_MULTICAST ) ++ if (status & RS_MULTICAST) + lp->stats.multicast++; + +- skb = dev_alloc_skb( packet_length + 5); ++ skb = dev_alloc_skb(packet_length + 5); + +- if ( skb == NULL ) { +- printk(KERN_NOTICE CARDNAME ": Low memory, packet dropped.\n"); ++ if (skb == NULL) { ++ printk(KERN_NOTICE "%s: Low memory, packet dropped.\n", ++ dev->name); + lp->stats.rx_dropped++; + goto done; + } +@@ -1330,36 +1789,15 @@ + ! in the worse case + */ + +- skb_reserve( skb, 2 ); /* 16 bit alignment */ ++ skb_reserve(skb, 2); /* 16 bit alignment */ + + skb->dev = dev; +- data = skb_put( skb, packet_length); ++ data = skb_put(skb, packet_length); + +-#ifdef USE_32_BIT +- /* QUESTION: Like in the TX routine, do I want +- to send the DWORDs or the bytes first, or some +- mixture. A mixture might improve already slow PIO +- performance */ +- PRINTK3((" Reading %d dwords (and %d bytes) \n", +- packet_length >> 2, packet_length & 3 )); +- insl(ioaddr + DATA_1 , data, packet_length >> 2 ); +- /* read the left over bytes */ +- insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC), +- packet_length & 0x3 ); +-#else +- PRINTK3((" Reading %d words and %d byte(s) \n", +- (packet_length >> 1 ), packet_length & 1 )); +- insw(ioaddr + DATA_1 , data, packet_length >> 1); +- if ( packet_length & 1 ) { +- data += packet_length & ~1; +- *(data++) = inb( ioaddr + DATA_1 ); +- } +-#endif +-#if SMC_DEBUG > 2 +- print_packet( data, packet_length ); +-#endif ++ smc_ins(ioaddr, DATA_1, data, packet_length); ++ print_packet(data, packet_length); + +- skb->protocol = eth_type_trans(skb, dev ); ++ skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + lp->stats.rx_packets++; +@@ -1368,15 +1806,17 @@ + /* error ... */ + lp->stats.rx_errors++; + +- if ( status & RS_ALGNERR ) lp->stats.rx_frame_errors++; +- if ( status & (RS_TOOSHORT | RS_TOOLONG ) ) ++ if (status & RS_ALGNERR) ++ lp->stats.rx_frame_errors++; ++ if (status & (RS_TOOSHORT | RS_TOOLONG)) + lp->stats.rx_length_errors++; +- if ( status & RS_BADCRC) lp->stats.rx_crc_errors++; ++ if (status & RS_BADCRC) ++ lp->stats.rx_crc_errors++; + } + + done: + /* error or good, tell the card to get rid of this packet */ +- outw( MC_RELEASE, ioaddr + MMU_CMD ); ++ smc_outw(MC_RELEASE, ioaddr, MMU_CMD); + } + + +@@ -1389,62 +1829,64 @@ + . Algorithm: + . Save pointer and packet no + . Get the packet no from the top of the queue +- . check if it's valid ( if not, is this an error??? ) ++ . check if it's valid (if not, is this an error???) + . read the status word + . record the error +- . ( resend? Not really, since we don't want old packets around ) ++ . (resend? Not really, since we don't want old packets around) + . Restore saved values + ************************************************************************/ +-static void smc_tx( struct net_device * dev ) ++static void smc_tx(struct net_device * dev) + { +- int ioaddr = dev->base_addr; ++ u_int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + byte saved_packet; + byte packet_no; + word tx_status; + + +- /* assume bank 2 */ ++ /* assume bank 2 */ + +- saved_packet = inb( ioaddr + PNR_ARR ); +- packet_no = inw( ioaddr + FIFO_PORTS ); ++ saved_packet = smc_inb(ioaddr, PNR_ARR); ++ packet_no = smc_inw(ioaddr, FIFO_PORTS); + packet_no &= 0x7F; + + /* select this as the packet to read from */ +- outb( packet_no, ioaddr + PNR_ARR ); ++ smc_outb(packet_no, ioaddr, PNR_ARR); + + /* read the first word from this packet */ +- outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER ); ++ smc_outw(PTR_AUTOINC | PTR_READ, ioaddr, POINTER); + +- tx_status = inw( ioaddr + DATA_1 ); +- PRINTK3((CARDNAME": TX DONE STATUS: %4x \n", tx_status )); ++ tx_status = smc_inw(ioaddr, DATA_1); ++ PRINTK3(("%s: TX DONE STATUS: %4x\n", dev->name, tx_status)); + + lp->stats.tx_errors++; +- if ( tx_status & TS_LOSTCAR ) lp->stats.tx_carrier_errors++; +- if ( tx_status & TS_LATCOL ) { +- printk(KERN_DEBUG CARDNAME +- ": Late collision occurred on last xmit.\n"); ++ if (tx_status & TS_LOSTCAR) ++ lp->stats.tx_carrier_errors++; ++ if (tx_status & TS_LATCOL) { ++ printk(KERN_DEBUG "%s: Late collision occurred on " ++ "last xmit.\n", dev->name); + lp->stats.tx_window_errors++; + } + #if 0 +- if ( tx_status & TS_16COL ) { ... } ++ if (tx_status & TS_16COL) { ... } + #endif + +- if ( tx_status & TS_SUCCESS ) { +- printk(CARDNAME": Successful packet caused interrupt \n"); ++ if (tx_status & TS_SUCCESS) { ++ printk("%s: Successful packet caused interrupt\n", ++ dev->name); + } + /* re-enable transmit */ +- SMC_SELECT_BANK( 0 ); +- outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR ); ++ SMC_SELECT_BANK(0); ++ smc_outw(smc_inw(ioaddr, TCR) | TCR_ENABLE, ioaddr, TCR); + + /* kill the packet */ +- SMC_SELECT_BANK( 2 ); +- outw( MC_FREEPKT, ioaddr + MMU_CMD ); ++ SMC_SELECT_BANK(2); ++ smc_outw(MC_FREEPKT, ioaddr, MMU_CMD); + + /* one less packet waiting for me */ + lp->packets_waiting--; + +- outb( saved_packet, ioaddr + PNR_ARR ); ++ smc_outb(saved_packet, ioaddr, PNR_ARR); + return; + } + +@@ -1460,7 +1902,7 @@ + { + netif_stop_queue(dev); + /* clear everything */ +- smc_shutdown( dev->base_addr ); ++ smc_shutdown(dev); + + /* Update the statistics here. */ + return 0; +@@ -1481,16 +1923,16 @@ + . + . This routine will, depending on the values passed to it, + . either make it accept multicast packets, go into +- . promiscuous mode ( for TCPDUMP and cousins ) or accept ++ . promiscuous mode (for TCPDUMP and cousins) or accept + . a select set of multicast packets + */ + static void smc_set_multicast_list(struct net_device *dev) + { +- short ioaddr = dev->base_addr; ++ u_int ioaddr = dev->base_addr; + + SMC_SELECT_BANK(0); +- if ( dev->flags & IFF_PROMISC ) +- outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR ); ++ if (dev->flags & IFF_PROMISC) ++ smc_outw(smc_inw(ioaddr, RCR) | RCR_PROMISC, ioaddr, RCR); + + /* BUG? I never disable promiscuous mode if multicasting was turned on. + Now, I turn off promiscuous mode, but I don't do anything to multicasting +@@ -1502,34 +1944,34 @@ + checked before the table is + */ + else if (dev->flags & IFF_ALLMULTI) +- outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR ); ++ smc_outw(smc_inw(ioaddr, RCR) | RCR_ALMUL, ioaddr, RCR); + + /* We just get all multicast packets even if we only want them + . from one source. This will be changed at some future + . point. */ +- else if (dev->mc_count ) { ++ else if (dev->mc_count) { + /* support hardware multicasting */ + + /* be sure I get rid of flags I might have set */ +- outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL), +- ioaddr + RCR ); ++ smc_outw(smc_inw(ioaddr, RCR) & ~(RCR_PROMISC | RCR_ALMUL), ++ ioaddr, RCR); + /* NOTE: this has to set the bank, so make sure it is the + last thing called. The bank is set to zero at the top */ +- smc_setmulticast( ioaddr, dev->mc_count, dev->mc_list ); ++ smc_setmulticast(dev, dev->mc_count, dev->mc_list); + } +- else { +- outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL), +- ioaddr + RCR ); ++ else { ++ smc_outw(smc_inw(ioaddr, RCR) & ~(RCR_PROMISC | RCR_ALMUL), ++ ioaddr, RCR); + + /* + since I'm disabling all multicast entirely, I need to + clear the multicast list + */ +- SMC_SELECT_BANK( 3 ); +- outw( 0, ioaddr + MULTICAST1 ); +- outw( 0, ioaddr + MULTICAST2 ); +- outw( 0, ioaddr + MULTICAST3 ); +- outw( 0, ioaddr + MULTICAST4 ); ++ SMC_SELECT_BANK(3); ++ smc_outw(0, ioaddr, MULTICAST1); ++ smc_outw(0, ioaddr, MULTICAST2); ++ smc_outw(0, ioaddr, MULTICAST3); ++ smc_outw(0, ioaddr, MULTICAST4); + } + } + +@@ -1550,21 +1992,26 @@ + + int init_module(void) + { +- int result; +- + if (io == 0) +- printk(KERN_WARNING +- CARDNAME": You shouldn't use auto-probing with insmod!\n" ); ++ printk(KERN_WARNING CARDNAME ++ ": You shouldn't use auto-probing with insmod!\n"); ++ ++ /* ++ * Note: dev->if_port has changed to be 2.4 compliant. ++ * We keep the ifport insmod parameter the same though. ++ */ ++ switch (ifport) { ++ case 1: devSMC9194.if_port = IF_PORT_10BASET; break; ++ case 2: devSMC9194.if_port = IF_PORT_AUI; break; ++ default: devSMC9194.if_port = 0; break; ++ } + + /* copy the parameters from insmod into the device structure */ + devSMC9194.base_addr = io; + devSMC9194.irq = irq; +- devSMC9194.if_port = ifport; +- devSMC9194.init = smc_init; +- if ((result = register_netdev(&devSMC9194)) != 0) +- return result; ++ devSMC9194.init = smc_init; + +- return 0; ++ return register_netdev(&devSMC9194); + } + + void cleanup_module(void) +diff -urN linux-2.4.26/drivers/net/smc9194.h linux-2.4.26-vrs1/drivers/net/smc9194.h +--- linux-2.4.26/drivers/net/smc9194.h 2001-09-08 20:13:55.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/net/smc9194.h 2004-01-14 21:32:26.000000000 +0000 +@@ -63,10 +63,11 @@ + + #define TCR 0 /* transmit control register */ + #define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */ ++#define TCR_PAD_ENABLE 0x0080 /* pads short packets to 64 bytes */ ++#define TCR_MON_CNS 0x0400 /* monitors the carrier status */ + #define TCR_FDUPLX 0x0800 /* receive packets sent out */ + #define TCR_STP_SQET 0x1000 /* stop transmitting if Signal quality error */ +-#define TCR_MON_CNS 0x0400 /* monitors the carrier status */ +-#define TCR_PAD_ENABLE 0x0080 /* pads short packets to 64 bytes */ ++#define TCR_FDSE 0x8000 /* full duplex, switched ethernet */ + + #define TCR_CLEAR 0 /* do NOTHING */ + /* the normal settings for the TCR register : */ +@@ -107,7 +108,10 @@ + #define CTL_CR_ENABLE 0x40 + #define CTL_TE_ENABLE 0x0020 + #define CTL_AUTO_RELEASE 0x0800 +-#define CTL_EPROM_ACCESS 0x0003 /* high if Eprom is being read */ ++#define CTL_EPROM_SELECT 0x0004 ++#define CTL_EPROM_RELOAD 0x0002 ++#define CTL_EPROM_STORE 0x0001 ++#define CTL_EPROM_ACCESS (CTL_EPROM_RELOAD | CTL_EPROM_STORE) /* high if Eprom is being read */ + + /* BANK 2 */ + #define MMU_CMD 0 +@@ -130,7 +134,6 @@ + #define PTR_READ 0x2000 + #define PTR_RCV 0x8000 + #define PTR_AUTOINC 0x4000 +-#define PTR_AUTO_INC 0x0040 + + #define DATA_1 8 + #define DATA_2 10 +@@ -162,17 +165,6 @@ + #define CHIP_9195 5 + #define CHIP_91100 7 + +-static const char * chip_ids[ 15 ] = { +- NULL, NULL, NULL, +- /* 3 */ "SMC91C90/91C92", +- /* 4 */ "SMC91C94", +- /* 5 */ "SMC91C95", +- NULL, +- /* 7 */ "SMC91C100", +- /* 8 */ "SMC91C100FD", +- NULL, NULL, NULL, +- NULL, NULL, NULL}; +- + /* + . Transmit status bits + */ +@@ -192,40 +184,20 @@ + #define RS_MULTICAST 0x0001 + #define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) + +-static const char * interfaces[ 2 ] = { "TP", "AUI" }; +- +-/*------------------------------------------------------------------------- +- . I define some macros to make it easier to do somewhat common +- . or slightly complicated, repeated tasks. +- --------------------------------------------------------------------------*/ +- +-/* select a register bank, 0 to 3 */ +- +-#define SMC_SELECT_BANK(x) { outw( x, ioaddr + BANK_SELECT ); } +- +-/* define a small delay for the reset */ +-#define SMC_DELAY() { inw( ioaddr + RCR );\ +- inw( ioaddr + RCR );\ +- inw( ioaddr + RCR ); } +- +-/* this enables an interrupt in the interrupt mask register */ +-#define SMC_ENABLE_INT(x) {\ +- unsigned char mask;\ +- SMC_SELECT_BANK(2);\ +- mask = inb( ioaddr + INT_MASK );\ +- mask |= (x);\ +- outb( mask, ioaddr + INT_MASK ); \ +-} +- +-/* this disables an interrupt from the interrupt mask register */ +- +-#define SMC_DISABLE_INT(x) {\ +- unsigned char mask;\ +- SMC_SELECT_BANK(2);\ +- mask = inb( ioaddr + INT_MASK );\ +- mask &= ~(x);\ +- outb( mask, ioaddr + INT_MASK ); \ +-} ++/* ++ * SMC91C96 ethernet config and status registers. ++ * These are in the "attribute" space. ++ */ ++#define ECOR 0x8000 ++#define ECOR_RESET 0x80 ++#define ECOR_LEVEL_IRQ 0x40 ++#define ECOR_WR_ATTRIB 0x04 ++#define ECOR_ENABLE 0x01 ++ ++#define ECSR 0x8002 ++#define ECSR_IOIS8 0x20 ++#define ECSR_PWRDWN 0x04 ++#define ECSR_INT 0x02 + + /*---------------------------------------------------------------------- + . Define the interrupts that I want to receive from the card +@@ -237,5 +209,36 @@ + --------------------------------------------------------------------------*/ + #define SMC_INTERRUPT_MASK (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) + ++/* store this information for the driver.. */ ++struct smc_local { ++ /* ++ these are things that the kernel wants me to keep, so users ++ can find out semi-useless statistics of how well the card is ++ performing ++ */ ++ struct net_device_stats stats; ++ ++ /* ++ If I have to wait until memory is available to send ++ a packet, I will store the skbuff here, until I get the ++ desired memory. Then, I'll send it out and free it. ++ */ ++ struct sk_buff * saved_skb; ++ ++ /* ++ . This keeps track of how many packets that I have ++ . sent out. When an TX_EMPTY interrupt comes, I know ++ . that all of these have been sent. ++ */ ++ int packets_waiting; ++ ++ /* ++ . Interface status. These correspond to the parameters ++ . in the ethtool_cmd structure. ++ */ ++ u8 duplex; ++ u8 port; ++}; ++ + #endif /* _SMC_9194_H_ */ + +diff -urN linux-2.4.26/drivers/parport/Config.in linux-2.4.26-vrs1/drivers/parport/Config.in +--- linux-2.4.26/drivers/parport/Config.in 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/parport/Config.in 2004-02-23 23:33:11.000000000 +0000 +@@ -27,7 +27,8 @@ + dep_tristate ' Support for PCMCIA management for PC-style ports' CONFIG_PARPORT_PC_PCMCIA $CONFIG_PCMCIA $CONFIG_PARPORT_PC $CONFIG_HOTPLUG + fi + if [ "$CONFIG_ARM" = "y" ]; then +- dep_tristate ' Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT ++ dep_tristate ' Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT $CONFIG_ARCH_ARC ++ dep_tristate ' Accelent SA1110 IDP' CONFIG_PARPORT_IDP $CONFIG_PARPORT $CONFIG_SA1100_ACCELENT + fi + if [ "$CONFIG_AMIGA" = "y" ]; then + dep_tristate ' Amiga builtin port' CONFIG_PARPORT_AMIGA $CONFIG_PARPORT +diff -urN linux-2.4.26/drivers/parport/Makefile linux-2.4.26-vrs1/drivers/parport/Makefile +--- linux-2.4.26/drivers/parport/Makefile 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/parport/Makefile 2004-02-23 23:17:13.000000000 +0000 +@@ -29,6 +29,7 @@ + obj-$(CONFIG_PARPORT_ATARI) += parport_atari.o + obj-$(CONFIG_PARPORT_SUNBPP) += parport_sunbpp.o + obj-$(CONFIG_PARPORT_GSC) += parport_gsc.o ++obj-$(CONFIG_PARPORT_IDP) += parport_idp.o + obj-$(CONFIG_PARPORT_IP22) += parport_ip22.o + + include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/parport/init.c linux-2.4.26-vrs1/drivers/parport/init.c +--- linux-2.4.26/drivers/parport/init.c 2002-11-28 23:53:14.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/parport/init.c 2004-01-14 21:32:26.000000000 +0000 +@@ -164,6 +164,9 @@ + #ifdef CONFIG_PARPORT_SUNBPP + parport_sunbpp_init(); + #endif ++#ifdef CONFIG_PARPORT_IDP ++ parport_idp_init(); ++#endif + return 0; + } + +diff -urN linux-2.4.26/drivers/parport/parport_idp.c linux-2.4.26-vrs1/drivers/parport/parport_idp.c +--- linux-2.4.26/drivers/parport/parport_idp.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/parport/parport_idp.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,247 @@ ++/* Low-level polled-mode parallel port routines for the Accelent IDP ++ * ++ * Author: Rich Dulabahn ++ * ++ * Inspiration taken from parport_amiga.c and parport_atari.c. ++ * ++ * To use, under menuconfig: ++ * 1) Turn on <*> Accelent IDP under Parallel port setup ++ * 2) Turn on <*> Parallel printer support under Character devices ++ * ++ * This will give you parport0 configured as /dev/lp0 ++ * ++ * To make the correct /dev/lp* entries, enter /dev and type this: ++ * ++ * mknod lp0 c 6 0 ++ * mknod lp1 c 6 1 ++ * mknod lp2 c 6 2 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * Parallel data port is port H, data ++ * Parallel data direction is port H, direction ++ * Control port is port I, data, lowest 4 bits ++ * Status port is port G, data, upper 5 bits ++ */ ++ ++#define INPUTPOWERHANDLER 0 ++/* masks */ ++#define CONTROL_MASK 0x0f ++#define STATUS_MASK 0xf8 ++ ++#undef DEBUG ++ ++#ifdef DEBUG ++#define DPRINTK printk ++#else ++#define DPRINTK(stuff...) ++#endif ++ ++static struct parport *this_port = NULL; ++ ++static unsigned char ++parport_idp_read_data(struct parport *p) ++{ ++ unsigned char c; ++ ++ c = IDP_FPGA_PORTH_DATA; ++ DPRINTK("read_data:0x%x\n",c); ++ return c; ++} ++ ++static void ++parport_idp_write_data(struct parport *p, unsigned char data) ++{ ++ IDP_FPGA_PORTH_DATA = data; ++ DPRINTK("write_data:0x%x\n",data); ++} ++ ++static unsigned char ++parport_idp_read_control(struct parport *p) ++{ ++ unsigned char c; ++ ++ c = IDP_FPGA_PORTI_DATA & CONTROL_MASK; ++ DPRINTK("read_control:0x%x\n",c); ++ return c; ++} ++ ++static void ++parport_idp_write_control(struct parport *p, unsigned char control) ++{ ++ unsigned int temp; ++ ++ temp = IDP_FPGA_PORTH_DATA; ++ temp &= ~CONTROL_MASK; ++ IDP_FPGA_PORTI_DATA = (temp | (control & CONTROL_MASK)); ++DPRINTK("write_control:0x%x\n",control); ++} ++ ++static unsigned char ++parport_idp_frob_control(struct parport *p, unsigned char mask, ++ unsigned char val) ++{ ++ unsigned char c; ++ ++/* From the parport-lowlevel.txt file...*/ ++/* This is equivalent to reading from the control register, masking out ++the bits in mask, exclusive-or'ing with the bits in val, and writing ++the result to the control register. */ ++ ++/* Easy enough, right? */ ++ ++ c = parport_idp_read_control(p); ++ parport_idp_write_control(p, (c & ~mask) ^ val); ++ DPRINTK("frob_control:0x%x\n",c); ++ return c; ++} ++ ++static unsigned char ++parport_idp_read_status(struct parport *p) ++{ ++ unsigned char c; ++ ++ c = IDP_FPGA_PORTG_DATA & STATUS_MASK; ++ c ^= 0x80; /* toggle S7 bit, active low */ ++ DPRINTK("read_status:0x%x\n",c); ++ return c; ++} ++ ++static void ++parport_idp_init_state(struct pardevice *d, struct parport_state *s) ++{ ++} ++ ++static void ++parport_idp_save_state(struct parport *p, struct parport_state *s) ++{ ++} ++ ++static void ++parport_idp_restore_state(struct parport *p, struct parport_state *s) ++{ ++} ++ ++static void ++parport_idp_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++} ++ ++static void ++parport_idp_enable_irq(struct parport *p) ++{ ++} ++ ++static void ++parport_idp_disable_irq(struct parport *p) ++{ ++} ++ ++static void ++parport_idp_data_forward(struct parport *p) ++{ ++ IDP_FPGA_PORTH_DIR = 0x00; /* 0 sets to output */ ++ DPRINTK("data_forward:0x%x\n",0); ++} ++ ++static void ++parport_idp_data_reverse(struct parport *p) ++{ ++ IDP_FPGA_PORTH_DIR = 0xff; /* and 1 sets to input */ ++ DPRINTK("data_reverse:0x%x\n",0xff); ++} ++ ++static void ++parport_idp_inc_use_count(void) ++{ ++ MOD_INC_USE_COUNT; ++} ++ ++static void ++parport_idp_dec_use_count(void) ++{ ++ MOD_DEC_USE_COUNT; ++} ++ ++static struct parport_operations parport_idp_ops = { ++ parport_idp_write_data, ++ parport_idp_read_data, ++ ++ parport_idp_write_control, ++ parport_idp_read_control, ++ parport_idp_frob_control, ++ ++ parport_idp_read_status, ++ ++ parport_idp_enable_irq, ++ parport_idp_disable_irq, ++ ++ parport_idp_data_forward, ++ parport_idp_data_reverse, ++ ++ parport_idp_init_state, ++ parport_idp_save_state, ++ parport_idp_restore_state, ++ ++ parport_idp_inc_use_count, ++ parport_idp_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, ++}; ++ ++ ++int __init ++parport_idp_init(void) ++{ ++ struct parport *p; ++ ++ p = parport_register_port((unsigned long)0,PARPORT_IRQ_NONE,PARPORT_DMA_NONE,&parport_idp_ops); ++ ++ if (!p) return 0; /* return 0 on failure */ ++ ++ this_port=p; ++ printk("%s: Accelent IDP parallel port registered.\n", p->name); ++ parport_proc_register(p); ++ parport_announce_port(p); ++ ++ return 1; ++} ++ ++#ifdef MODULE ++ ++MODULE_AUTHOR("Rich Dulabahn"); ++MODULE_DESCRIPTION("Parport Driver for Accelent IDP"); ++MODULE_SUPPORTED_DEVICE("Accelent IDP builtin Parallel Port"); ++MODULE_LICENSE("GPL"); ++ ++int ++init_module(void) ++{ ++ return parport_idp_init() ? 0 : -ENODEV; ++} ++ ++void ++cleanup_module(void) ++{ ++ parport_proc_unregister(this_port); ++ parport_unregister_port(this_port); ++} ++#endif ++ +diff -urN linux-2.4.26/drivers/pci/Makefile linux-2.4.26-vrs1/drivers/pci/Makefile +--- linux-2.4.26/drivers/pci/Makefile 2003-08-25 12:44:42.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pci/Makefile 2004-01-14 21:39:07.000000000 +0000 +@@ -13,7 +13,7 @@ + + export-objs := pci.o + +-obj-$(CONFIG_PCI) += pci.o quirks.o compat.o names.o ++obj-$(CONFIG_PCI) += pci.o quirks.o compat.o names.o bridge.o + obj-$(CONFIG_PROC_FS) += proc.o + + ifndef CONFIG_SPARC64 +diff -urN linux-2.4.26/drivers/pci/bridge.c linux-2.4.26-vrs1/drivers/pci/bridge.c +--- linux-2.4.26/drivers/pci/bridge.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pci/bridge.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,149 @@ ++ ++/* ++ * Copyright (c) 2001 Red Hat, Inc. All rights reserved. ++ * ++ * This software may be freely redistributed under the terms ++ * of the GNU public license. ++ * ++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Author: Arjan van de Ven ++ * ++ */ ++ ++ ++/* ++ * Generic PCI driver for PCI bridges for powermanagement purposes ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static struct pci_device_id bridge_pci_table[] __devinitdata = { ++ {/* handle all PCI bridges */ ++ class: ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ++ class_mask: ~0, ++ vendor: PCI_ANY_ID, ++ device: PCI_ANY_ID, ++ subvendor: PCI_ANY_ID, ++ subdevice: PCI_ANY_ID, ++ }, ++ {0,}, ++}; ++ ++static int bridge_probe(struct pci_dev *pdev, const struct pci_device_id *id); ++static int pci_bridge_save_state_bus(struct pci_bus *bus, int force); ++int pci_generic_resume_compare(struct pci_dev *pdev); ++ ++int pci_bridge_force_restore = 0; ++ ++ ++ ++ ++static int __init bridge_setup(char *str) ++{ ++ if (!strcmp(str,"force")) ++ pci_bridge_force_restore = 1; ++ else if (!strcmp(str,"noforce")) ++ pci_bridge_force_restore = 0; ++ return 0; ++} ++ ++__setup("resume=",bridge_setup); ++ ++ ++static int pci_bridge_save_state_bus(struct pci_bus *bus, int force) ++{ ++ struct list_head *list; ++ int error = 0; ++ ++ list_for_each(list, &bus->children) { ++ error = pci_bridge_save_state_bus(pci_bus_b(list),force); ++ if (error) return error; ++ } ++ list_for_each(list, &bus->devices) { ++ pci_generic_suspend_save(pci_dev_b(list),0); ++ } ++ return 0; ++} ++ ++ ++static int pci_bridge_restore_state_bus(struct pci_bus *bus, int force) ++{ ++ struct list_head *list; ++ int error = 0; ++ static int printed_warning=0; ++ ++ list_for_each(list, &bus->children) { ++ error = pci_bridge_restore_state_bus(pci_bus_b(list),force); ++ if (error) return error; ++ } ++ list_for_each(list, &bus->devices) { ++ if (force) ++ pci_generic_resume_restore(pci_dev_b(list)); ++ else { ++ error = pci_generic_resume_compare(pci_dev_b(list)); ++ if (error && !printed_warning++) { ++ printk(KERN_WARNING "resume warning: bios doesn't restore PCI state properly\n"); ++ printk(KERN_WARNING "resume warning: if resume failed, try booting with resume=force\n"); ++ } ++ if (error) ++ return error; ++ } ++ } ++ return 0; ++} ++ ++static int bridge_suspend(struct pci_dev *dev, u32 force) ++{ ++ pci_generic_suspend_save(dev,force); ++ if (dev->subordinate) ++ pci_bridge_save_state_bus(dev->subordinate,force); ++ return 0; ++} ++ ++static int bridge_resume(struct pci_dev *dev) ++{ ++ ++ pci_generic_resume_restore(dev); ++ if (dev->subordinate) ++ pci_bridge_restore_state_bus(dev->subordinate,pci_bridge_force_restore); ++ return 0; ++} ++ ++ ++MODULE_DEVICE_TABLE(pci, bridge_pci_table); ++static struct pci_driver bridge_ops = { ++ name: "PCI Bridge", ++ id_table: bridge_pci_table, ++ probe: bridge_probe, ++ suspend: bridge_suspend, ++ resume: bridge_resume ++}; ++ ++static int __devinit bridge_probe(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ return 0; ++} ++ ++static int __init bridge_init(void) ++{ ++ pci_register_driver(&bridge_ops); ++ return 0; ++} ++ ++static void __exit bridge_exit(void) ++{ ++ pci_unregister_driver(&bridge_ops); ++} ++ ++ ++module_init(bridge_init) ++module_exit(bridge_exit) ++ +diff -urN linux-2.4.26/drivers/pci/pci.c linux-2.4.26-vrs1/drivers/pci/pci.c +--- linux-2.4.26/drivers/pci/pci.c 2003-11-28 18:26:20.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/pci/pci.c 2004-01-14 21:39:08.000000000 +0000 +@@ -359,6 +359,48 @@ + return 0; + } + ++int ++pci_compare_state(struct pci_dev *dev, u32 *buffer) ++{ ++ int i; ++ unsigned int temp; ++ ++ if (buffer) { ++ for (i = 0; i < 16; i++) { ++ pci_read_config_dword(dev,i*4,&temp); ++ if (temp!=buffer[i]) ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++int pci_generic_suspend_save(struct pci_dev *pdev, u32 state) ++{ ++ if (pdev) ++ pci_save_state(pdev,pdev->saved_state); ++ return 0; ++} ++ ++int pci_generic_resume_restore(struct pci_dev *pdev) ++{ ++ if (pdev) ++ pci_restore_state(pdev,pdev->saved_state); ++ return 0; ++} ++ ++int pci_generic_resume_compare(struct pci_dev *pdev) ++{ ++ int retval=0; ++ if (pdev) ++ retval = pci_compare_state(pdev,pdev->saved_state); ++ return retval; ++} ++ ++EXPORT_SYMBOL(pci_generic_suspend_save); ++EXPORT_SYMBOL(pci_generic_resume_restore); ++EXPORT_SYMBOL(pci_generic_resume_compare); ++ + /** + * pci_enable_device_bars - Initialize some of a device for use + * @dev: PCI device to be initialized +diff -urN linux-2.4.26/drivers/pci/setup-bus.c linux-2.4.26-vrs1/drivers/pci/setup-bus.c +--- linux-2.4.26/drivers/pci/setup-bus.c 2003-06-13 15:51:35.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pci/setup-bus.c 2004-01-14 21:32:26.000000000 +0000 +@@ -12,6 +12,8 @@ + /* + * Nov 2000, Ivan Kokshaysky + * PCI-PCI bridges cleanup, sorted resource allocation. ++ * May 2001, Russell King ++ * Allocate prefetchable memory regions where available. + * Feb 2002, Ivan Kokshaysky + * Converted to allocation in 3 passes, which gives + * tighter packing. Prefetchable range support. +@@ -160,8 +162,10 @@ + pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l); + + /* Check if we have VGA behind the bridge. +- Enable ISA in either case (FIXME!). */ +- l = (bus->resource[0]->flags & IORESOURCE_BUS_HAS_VGA) ? 0x0c : 0x04; ++ Enable ISA in either case. */ ++ l = (bus->resource[0]->flags & IORESOURCE_BUS_HAS_VGA) ? ++ PCI_BRIDGE_CTL_VGA | PCI_BRIDGE_CTL_NO_ISA : ++ PCI_BRIDGE_CTL_NO_ISA; + pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, l); + } + +diff -urN linux-2.4.26/drivers/pcmcia/Config.in linux-2.4.26-vrs1/drivers/pcmcia/Config.in +--- linux-2.4.26/drivers/pcmcia/Config.in 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/pcmcia/Config.in 2004-02-23 23:29:38.000000000 +0000 +@@ -14,21 +14,19 @@ + + tristate 'PCMCIA/CardBus support' CONFIG_PCMCIA + if [ "$CONFIG_PCMCIA" != "n" ]; then ++ # yes, I really mean the following... ++ if [ "$CONFIG_ISA" = "y" -o "$CONFIG_ARCH_SA1100" = "y" ]; then ++ define_bool CONFIG_PCMCIA_PROBE y ++ fi + if [ "$CONFIG_PCI" != "n" ]; then + bool ' CardBus support' CONFIG_CARDBUS + fi ++ dep_bool ' i82092 compatible bridge support' CONFIG_I82092 $CONFIG_PCI ++ bool ' i82365 compatible bridge support' CONFIG_I82365 + bool ' Databook TCIC host bridge support' CONFIG_TCIC + if [ "$CONFIG_HD64465" = "y" ]; then + dep_tristate ' HD64465 host bridge support' CONFIG_HD64465_PCMCIA $CONFIG_PCMCIA + fi +- dep_bool ' i82092 compatible bridge support' CONFIG_I82092 $CONFIG_PCI +- bool ' i82365 compatible bridge support' CONFIG_I82365 +- if [ "$CONFIG_ARCH_SA1100" = "y" ]; then +- dep_tristate ' SA1100 support' CONFIG_PCMCIA_SA1100 $CONFIG_PCMCIA +- fi +- if [ "$CONFIG_8xx" = "y" ]; then +- dep_tristate ' M8xx support' CONFIG_PCMCIA_M8XX $CONFIG_PCMCIA +- fi + if [ "$CONFIG_SOC_AU1X00" = "y" ]; then + dep_tristate ' Au1x00 PCMCIA support' CONFIG_PCMCIA_AU1X00 $CONFIG_PCMCIA + if [ "$CONFIG_PCMCIA_AU1X00" != "n" ]; then +@@ -44,5 +42,9 @@ + dep_tristate ' NEC VRC4173 CARDU support' CONFIG_PCMCIA_VRC4173 $CONFIG_PCMCIA + fi + fi ++if [ "$CONFIG_ARM" = "y" ]; then ++ dep_tristate ' CLPS6700 support' CONFIG_PCMCIA_CLPS6700 $CONFIG_ARCH_CLPS711X $CONFIG_PCMCIA ++ dep_tristate ' SA1100 support' CONFIG_PCMCIA_SA1100 $CONFIG_ARCH_SA1100 $CONFIG_PCMCIA ++fi + + endmenu +diff -urN linux-2.4.26/drivers/pcmcia/Makefile linux-2.4.26-vrs1/drivers/pcmcia/Makefile +--- linux-2.4.26/drivers/pcmcia/Makefile 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/pcmcia/Makefile 2004-02-23 23:19:09.000000000 +0000 +@@ -65,15 +65,18 @@ + au1000_ss-objs-$(CONFIG_PCMCIA_DB1X00) += au1000_db1x00.o + au1000_ss-objs-$(CONFIG_PCMCIA_XXS1500) += au1000_xxs1500.o + ++obj-$(CONFIG_PCMCIA_CLPS6700) += clps6700.o + obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o +-obj-$(CONFIG_PCMCIA_M8XX) += m8xx_pcmcia.o + obj-$(CONFIG_PCMCIA_SIBYTE) += sibyte_generic.o + + sa1100_cs-objs-y := sa1100_generic.o ++sa1100_cs-objs-$(CONFIG_SA1100_ADSAGC) += sa1100_graphicsmaster.o sa1111_generic.o + sa1100_cs-objs-$(CONFIG_SA1100_ADSBITSY) += sa1100_adsbitsy.o sa1111_generic.o ++sa1100_cs-objs-$(CONFIG_SA1100_ADSBITSYPLUS) += sa1100_adsbitsyplus.o sa1111_generic.o + sa1100_cs-objs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o + sa1100_cs-objs-$(CONFIG_ASSABET_NEPONSET) += sa1100_neponset.o sa1111_generic.o + sa1100_cs-objs-$(CONFIG_SA1100_BADGE4) += sa1100_badge4.o sa1111_generic.o ++sa1100_cs-objs-$(CONFIG_SA1100_CONSUS) += sa1100_neponset.o sa1111_generic.o + sa1100_cs-objs-$(CONFIG_SA1100_CERF) += sa1100_cerf.o + sa1100_cs-objs-$(CONFIG_SA1100_FLEXANET) += sa1100_flexanet.o + sa1100_cs-objs-$(CONFIG_SA1100_FREEBIRD) += sa1100_freebird.o +diff -urN linux-2.4.26/drivers/pcmcia/cistpl.c linux-2.4.26-vrs1/drivers/pcmcia/cistpl.c +--- linux-2.4.26/drivers/pcmcia/cistpl.c 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/pcmcia/cistpl.c 2004-02-23 13:36:32.000000000 +0000 +@@ -286,7 +286,7 @@ + s->cis_mem.flags &= ~MAP_ACTIVE; + s->ss_entry->set_mem_map(s->sock, &s->cis_mem); + if (!(s->cap.features & SS_CAP_STATIC_MAP)) +- release_mem_region(s->cis_mem.sys_start, s->cap.map_size); ++ release_mem_resource(s->cis_mem.sys_start, s->cap.map_size); + bus_iounmap(s->cap.bus, s->cis_virt); + s->cis_mem.sys_start = 0; + s->cis_virt = NULL; +diff -urN linux-2.4.26/drivers/pcmcia/clps6700.c linux-2.4.26-vrs1/drivers/pcmcia/clps6700.c +--- linux-2.4.26/drivers/pcmcia/clps6700.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/clps6700.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,498 @@ ++/* ++ * linux/drivers/pcmcia/clps6700.c ++ * ++ * Copyright (C) 2000 Deep Blue Solutions Ltd ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "clps6700.h" ++ ++#define DEBUG ++ ++MODULE_AUTHOR("Russell King"); ++MODULE_DESCRIPTION("CL-PS6700 PCMCIA socket driver"); ++ ++#define NR_CLPS6700 2 ++ ++struct clps6700_skt { ++ u_int nr; ++ u_int physbase; ++ u_int regbase; ++ u_int pmr; ++ u_int cpcr; ++ u_int cpcr_3v3; ++ u_int cpcr_5v0; ++ u_int cur_pmr; ++ u_int cur_cicr; ++ u_int cur_pcimr; ++ u_int cur_cpcr; ++ void (*handler)(void *, u_int); ++ void *handler_info; ++ ++ u_int ev_pending; ++ spinlock_t ev_lock; ++}; ++ ++static struct clps6700_skt *skts[NR_CLPS6700]; ++ ++static int clps6700_sock_init(u_int sock) ++{ ++ struct clps6700_skt *skt = skts[sock]; ++ ++ skt->cur_cicr = 0; ++ skt->cur_pmr = skt->pmr; ++ skt->cur_pcimr = 0; ++ skt->cur_cpcr = skt->cpcr; ++ ++#ifdef DEBUG ++ printk("skt%d: sock_init()\n", sock); ++#endif ++ ++ __raw_writel(skt->cur_pmr, skt->regbase + PMR); ++ __raw_writel(skt->cur_cpcr, skt->regbase + CPCR); ++ __raw_writel(0x01f8, skt->regbase + SICR); ++ __raw_writel(0x0000, skt->regbase + DMACR); ++ __raw_writel(skt->cur_cicr, skt->regbase + CICR); ++ __raw_writel(0x1f00, skt->regbase + CITR0A); ++ __raw_writel(0x0000, skt->regbase + CITR0B); ++ __raw_writel(0x1f00, skt->regbase + CITR1A); ++ __raw_writel(0x0000, skt->regbase + CITR1B); ++ __raw_writel(skt->cur_pcimr, skt->regbase + PCIMR); ++ ++ /* ++ * Enable Auto Idle Mode in PM register ++ */ ++ __raw_writel(-1, skt->regbase + PCIRR1); ++ __raw_writel(-1, skt->regbase + PCIRR2); ++ __raw_writel(-1, skt->regbase + PCIRR3); ++ ++ return 0; ++} ++ ++static int clps6700_suspend(u_int sock) ++{ ++ return 0; ++} ++ ++static int clps6700_register_callback(u_int sock, void (*handler)(void *, u_int), void *info) ++{ ++ struct clps6700_skt *skt = skts[sock]; ++ ++#ifdef DEBUG ++ printk("skt%d: register_callback: %p (%p)\n", sock, handler, info); ++#endif ++ ++ skt->handler_info = info; ++ skt->handler = handler; ++ ++ return 0; ++} ++ ++static int clps6700_inquire_socket(u_int sock, socket_cap_t *cap) ++{ ++ cap->features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP | SS_CAP_MEM_ALIGN; ++ cap->irq_mask = 0; /* available IRQs for this socket */ ++ cap->map_size = PAGE_SIZE; /* minimum mapping size */ ++ cap->pci_irq = 0; /* PCI interrupt number */ ++ cap->cb_dev = NULL; ++ cap->bus = NULL; ++ return 0; ++} ++ ++static int __clps6700_get_status(struct clps6700_skt *skt) ++{ ++ unsigned int v, val; ++ ++ v = __raw_readl(skt->regbase + PCIILR); ++ val = 0; ++ if ((v & (PCM_CD1 | PCM_CD2)) == 0) ++ val |= SS_DETECT; ++ if ((v & (PCM_BVD2 | PCM_BVD1)) == PCM_BVD1) ++ val |= SS_BATWARN; ++ if ((v & PCM_BVD2) == 0) ++ val |= SS_BATDEAD; ++ ++ if (v & PCM_RDYL) ++ val |= SS_READY; ++ if (v & PCM_VS1) ++ val |= SS_3VCARD; ++ if (v & PCM_VS2) ++ val |= SS_XVCARD; ++ ++#ifdef DEBUG ++ printk("skt%d: PCIILR: %08x -> (%s %s %s %s %s %s)\n", ++ skt->nr, v, ++ val & SS_READY ? "rdy" : "---", ++ val & SS_DETECT ? "det" : "---", ++ val & SS_BATWARN ? "bw" : "--", ++ val & SS_BATDEAD ? "bd" : "--", ++ val & SS_3VCARD ? "3v" : "--", ++ val & SS_XVCARD ? "xv" : "--"); ++#endif ++ return val; ++} ++ ++static int clps6700_get_status(u_int sock, u_int *valp) ++{ ++ struct clps6700_skt *skt = skts[sock]; ++ ++ *valp = __clps6700_get_status(skt); ++ ++ return 0; /* not used! */ ++} ++ ++static int clps6700_get_socket(u_int sock, socket_state_t *state) ++{ ++ return -EINVAL; ++} ++ ++static int clps6700_set_socket(u_int sock, socket_state_t *state) ++{ ++ struct clps6700_skt *skt = skts[sock]; ++ unsigned long flags; ++ u_int cpcr = skt->cur_cpcr, pmr = skt->cur_pmr, cicr = skt->cur_cicr; ++ u_int pcimr = 0; ++ ++ cicr &= ~(CICR_ENABLE | CICR_RESET | CICR_IOMODE); ++ ++ if (state->flags & SS_PWR_AUTO) ++ pmr |= PMR_DCAR | PMR_PDCR; ++ ++ /* ++ * Note! We must NOT assert the Card Enable bit until reset has ++ * been de-asserted. Some cards indicate not ready, which then ++ * hangs our next access. (Bug in CLPS6700?) ++ */ ++ if (state->flags & SS_RESET) ++ cicr |= CICR_RESET | CICR_RESETOE; ++ else if (state->flags & SS_OUTPUT_ENA) ++ cicr |= CICR_ENABLE; ++ ++ if (state->flags & SS_IOCARD) { ++ cicr |= CICR_IOMODE; ++ ++/* if (state->csc_mask & SS_STSCHG)*/ ++ } else { ++ if (state->csc_mask & SS_BATDEAD) ++ pcimr |= PCM_BVD2; ++ if (state->csc_mask & SS_BATWARN) ++ pcimr |= PCM_BVD1; ++ if (state->csc_mask & SS_READY) ++ pcimr |= PCM_RDYL; ++ } ++ ++ if (state->csc_mask & SS_DETECT) ++ pcimr |= PCM_CD1 | PCM_CD2; ++ ++ switch (state->Vcc) { ++ case 0: break; ++ case 33: cpcr |= skt->cpcr_3v3; pmr |= PMR_CPE; break; ++ case 50: cpcr |= skt->cpcr_5v0; pmr |= PMR_CPE; break; ++ default: return -EINVAL; ++ } ++ ++#ifdef DEBUG ++ printk("skt%d: PMR: %04x, CPCR: %04x, CICR: %04x PCIMR: %04x " ++ "(Vcc = %d, flags = %c%c%c%c, csc = %c%c%c%c%c)\n", ++ sock, pmr, cpcr, cicr, pcimr, state->Vcc, ++ state->flags & SS_RESET ? 'r' : '-', ++ state->flags & SS_PWR_AUTO ? 'p' : '-', ++ state->flags & SS_IOCARD ? 'i' : '-', ++ state->flags & SS_OUTPUT_ENA ? 'o' : '-', ++ state->csc_mask & SS_STSCHG ? 's' : '-', ++ state->csc_mask & SS_BATDEAD ? 'd' : '-', ++ state->csc_mask & SS_BATWARN ? 'w' : '-', ++ state->csc_mask & SS_READY ? 'r' : '-', ++ state->csc_mask & SS_DETECT ? 'c' : '-'); ++#endif ++ ++ save_flags_cli(flags); ++ ++ if (skt->cur_cpcr != cpcr) { ++ skt->cur_cpcr = cpcr; ++ __raw_writel(skt->cur_cpcr, skt->regbase + CPCR); ++ } ++ ++ if (skt->cur_pmr != pmr) { ++ skt->cur_pmr = pmr; ++ __raw_writel(skt->cur_pmr, skt->regbase + PMR); ++ } ++ if (skt->cur_pcimr != pcimr) { ++ skt->cur_pcimr = pcimr; ++ __raw_writel(skt->cur_pcimr, skt->regbase + PCIMR); ++ } ++ if (skt->cur_cicr != cicr) { ++ skt->cur_cicr = cicr; ++ __raw_writel(skt->cur_cicr, skt->regbase + CICR); ++ } ++ ++ restore_flags(flags); ++ ++ return 0; ++} ++ ++static int clps6700_get_io_map(u_int sock, struct pccard_io_map *io) ++{ ++ return -EINVAL; ++} ++ ++static int clps6700_set_io_map(u_int sock, struct pccard_io_map *io) ++{ ++ printk("skt%d: iomap: %d: speed %d, flags %X start %X stop %X\n", ++ sock, io->map, io->speed, io->flags, io->start, io->stop); ++ return 0; ++} ++ ++static int clps6700_get_mem_map(u_int sock, struct pccard_mem_map *mem) ++{ ++ return -EINVAL; ++} ++ ++/* ++ * Set the memory map attributes for this socket. (ie, mem->speed) ++ * Note that since we have SS_CAP_STATIC_MAP set, we don't need to do ++ * any mapping here at all; we just need to return the address (suitable ++ * for ioremap) to map the requested space in mem->sys_start. ++ * ++ * flags & MAP_ATTRIB indicates whether we want attribute space. ++ */ ++static int clps6700_set_mem_map(u_int sock, struct pccard_mem_map *mem) ++{ ++ struct clps6700_skt *skt = skts[sock]; ++ u_int off; ++ ++ printk("skt%d: memmap: %d: speed %d, flags %X start %lX stop %lX card %X\n", ++ sock, mem->map, mem->speed, mem->flags, mem->sys_start, ++ mem->sys_stop, mem->card_start); ++ ++ if (mem->flags & MAP_ATTRIB) ++ off = CLPS6700_ATTRIB_BASE; ++ else ++ off = CLPS6700_MEM_BASE; ++ ++ mem->sys_start = skt->physbase + off; ++ mem->sys_start += mem->card_start; ++ ++ return 0; ++} ++ ++static void clps6700_proc_setup(u_int sock, struct proc_dir_entry *base) ++{ ++} ++ ++static struct pccard_operations clps6700_operations = { ++ init: clps6700_sock_init, ++ suspend: clps6700_suspend, ++ register_callback: clps6700_register_callback, ++ inquire_socket: clps6700_inquire_socket, ++ get_status: clps6700_get_status, ++ get_socket: clps6700_get_socket, ++ set_socket: clps6700_set_socket, ++ get_io_map: clps6700_get_io_map, ++ set_io_map: clps6700_set_io_map, ++ get_mem_map: clps6700_get_mem_map, ++ set_mem_map: clps6700_set_mem_map, ++ proc_setup: clps6700_proc_setup ++}; ++ ++static void clps6700_bh(void *dummy) ++{ ++ int i; ++ ++ for (i = 0; i < NR_CLPS6700; i++) { ++ struct clps6700_skt *skt = skts[i]; ++ unsigned long flags; ++ u_int events; ++ ++ if (!skt) ++ continue; ++ ++ /* ++ * Note! We must read the pending event state ++ * with interrupts disabled, otherwise we race ++ * with our own interrupt routine! ++ */ ++ spin_lock_irqsave(&skt->ev_lock, flags); ++ events = skt->ev_pending; ++ skt->ev_pending = 0; ++ spin_unlock_irqrestore(&skt->ev_lock, flags); ++ ++ if (skt->handler && events) ++ skt->handler(skt->handler_info, events); ++ } ++} ++ ++static struct tq_struct clps6700_task = { ++ routine: clps6700_bh ++}; ++ ++static void clps6700_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct clps6700_skt *skt = dev_id; ++ u_int val, events; ++ ++ val = __raw_readl(skt->regbase + PCISR); ++ if (!val) ++ return; ++ ++ __raw_writel(val, skt->regbase + PCICR); ++ ++ events = 0; ++ if (val & (PCM_CD1 | PCM_CD2)) ++ events |= SS_DETECT; ++ if (val & PCM_BVD1) ++ events |= SS_BATWARN; ++ if (val & PCM_BVD2) ++ events |= SS_BATDEAD; ++ if (val & PCM_RDYL) ++ events |= SS_READY; ++ ++ spin_lock(&skt->ev_lock); ++ skt->ev_pending |= events; ++ spin_unlock(&skt->ev_lock); ++ schedule_task(&clps6700_task); ++} ++ ++static int __init clps6700_init_skt(int nr) ++{ ++ struct clps6700_skt *skt; ++ int ret; ++ ++ skt = kmalloc(sizeof(struct clps6700_skt), GFP_KERNEL); ++ if (!skt) ++ return -ENOMEM; ++ ++ memset(skt, 0, sizeof(struct clps6700_skt)); ++ ++ spin_lock_init(&skt->ev_lock); ++ ++ skt->nr = nr; ++ skt->physbase = nr ? CS5_PHYS_BASE : CS4_PHYS_BASE; ++ skt->pmr = PMR_AUTOIDLE | PMR_MCPE | PMR_CDWEAK; ++ skt->cpcr = CPCR_PDIR(PCTL1|PCTL0); ++ skt->cpcr_3v3 = CPCR_PON(PCTL0); ++ skt->cpcr_5v0 = CPCR_PON(PCTL0); /* we only do 3v3 */ ++ ++ skt->cur_pmr = skt->pmr; ++ ++ skt->regbase = (u_int)ioremap(skt->physbase + CLPS6700_REG_BASE, ++ CLPS6700_REG_SIZE); ++ ret = -ENOMEM; ++ if (!skt->regbase) ++ goto err_free; ++ ++ skts[nr] = skt; ++ ++ ret = request_irq(IRQ_EINT3, clps6700_interrupt, ++ SA_SHIRQ, "pcmcia", skt); ++ ++ if (ret) { ++ printk(KERN_ERR "clps6700: unable to grab irq%d (%d)\n", ++ IRQ_EINT3, ret); ++ goto err_unmap; ++ } ++ return 0; ++ ++err_unmap: ++ iounmap((void *)skt->regbase); ++err_free: ++ kfree(skt); ++ skts[nr] = NULL; ++ return ret; ++} ++ ++static void clps6700_free_resources(void) ++{ ++ int i; ++ ++ for (i = NR_CLPS6700; i >= 0; i--) { ++ struct clps6700_skt *skt = skts[i]; ++ ++ skts[i] = NULL; ++ if (skt == NULL) ++ continue; ++ ++ free_irq(IRQ_EINT3, skt); ++ if (skt->regbase) { ++ __raw_writel(skt->pmr, skt->regbase + PMR); ++ __raw_writel(skt->cpcr, skt->regbase + CPCR); ++ __raw_writel(0, skt->regbase + CICR); ++ __raw_writel(0, skt->regbase + PCIMR); ++ } ++ iounmap((void *)skt->regbase); ++ kfree(skt); ++ } ++} ++ ++static int __init clps6700_init(void) ++{ ++ unsigned int v; ++ int err, nr; ++ ++ PLD_CF = 0; ++ v = clps_readl(SYSCON2) | SYSCON2_PCCARD1 | SYSCON2_PCCARD2; ++ clps_writel(v, SYSCON2); ++ v = clps_readl(SYSCON1) | SYSCON1_EXCKEN; ++ clps_writel(v, SYSCON1); ++ ++ for (nr = 0; nr < NR_CLPS6700; nr++) { ++ err = clps6700_init_skt(nr); ++ if (err) ++ goto free; ++ } ++ ++ err = register_ss_entry(nr, &clps6700_operations); ++ if (err) ++ goto free; ++ ++ return 0; ++ ++free: ++ clps6700_free_resources(); ++ /* ++ * An error occurred. Unmap and free all CLPS6700 ++ */ ++ return err; ++} ++ ++static void __exit clps6700_exit(void) ++{ ++ unregister_ss_entry(&clps6700_operations); ++ clps6700_free_resources(); ++} ++ ++module_init(clps6700_init); ++module_exit(clps6700_exit); +diff -urN linux-2.4.26/drivers/pcmcia/clps6700.h linux-2.4.26-vrs1/drivers/pcmcia/clps6700.h +--- linux-2.4.26/drivers/pcmcia/clps6700.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/clps6700.h 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,85 @@ ++#define PCISR 0x0000 /* PC Card Interrupt Status Register */ ++#define PCIMR 0x0400 /* PC Card Interrupt Mask Register */ ++#define PCICR 0x0800 /* PC Card Interrupt Clear Register */ ++#define PCIOSR 0x0c00 /* PC Card Interrupt Output Select Regsiter */ ++#define PCIRR1 0x1000 /* PC Card Interrupt Reserved Register 1 */ ++#define PCIRR2 0x1400 /* PC Card Interrupt Reserved Register 2 */ ++#define PCIRR3 0x1800 /* PC Card Interrupt Reserved Register 3 */ ++#define PCIILR 0x1c00 /* PC Card Interrupt Input Level Register */ ++#define SICR 0x2000 /* System Interface Configuration Register */ ++#define CICR 0x2400 /* Card Interface Configuration Register */ ++#define PMR 0x2800 /* Power Management Register */ ++#define CPCR 0x2c00 /* Card Power Control Register */ ++#define CITR0A 0x3000 /* Card Interface Timing Register 0A */ ++#define CITR0B 0x3400 /* Card Interface Timing Register 0B */ ++#define CITR1A 0x3800 /* Card Interface Timing Register 1A */ ++#define CITR1B 0x3c00 /* Card Interface Timing Register 1B */ ++#define DMACR 0x4000 /* DMA Control Register */ ++#define DIR 0x4400 /* Device Information Register */ ++ ++#define CLPS6700_ATTRIB_BASE 0x00000000 ++#define CLPS6700_IO_BASE 0x04000000 ++#define CLPS6700_MEM_BASE 0x08000000 ++#define CLPS6700_REG_BASE 0x0c000000 ++#define CLPS6700_REG_SIZE 0x00005000 ++ ++ ++#define PMR_AUTOIDLE (1 << 0) /* auto idle mode */ ++#define PMR_FORCEIDLE (1 << 1) /* force idle mode */ ++#define PMR_PDCS (1 << 2) /* Power down card on standby */ ++#define PMR_PDCR (1 << 3) /* Power down card on removal */ ++#define PMR_DCAR (1 << 4) /* Disable card access on removal */ ++#define PMR_CPE (1 << 5) /* Card power enable */ ++#define PMR_MCPE (1 << 6) /* Monitor card power enable */ ++#define PMR_PDREQLSEL (1 << 7) /* If set, PDREQL is a GPIO pin */ ++#define PMR_DISSTBY (1 << 8) /* Disable standby */ ++#define PMR_ACCSTBY (1 << 9) /* Complete card accesses before standby*/ ++#define PMR_CDUNPROT (0 << 10) /* Card detect inputs unprotected */ ++#define PMR_CDPROT (1 << 10) /* Card detect inputs protected */ ++#define PMR_CDWEAK (2 << 10) /* Weak pullup except in standby */ ++#define PMR_CDWEAKAL (3 << 10) /* Weak pullup */ ++ ++#define CPCR_PON(x) ((x)&7) /* PCTL[2:0] value when PMR_CPE = 1 */ ++#define CPCR_POFF(x) (((x)&7)<<3) /* PCTL[2:0] value when PMR_CPE = 0 */ ++#define CPCR_PDIR(x) (((x)&7)<<6) /* PCTL[2:0] direction */ ++#define CPCR_CON(x) (((x)&1)<<9) /* GPIO value when PMR_CPE = 1 */ ++#define CPCR_COFF(x) (((x)&1)<<10) /* GPIO value when PMR_CPE = 0 */ ++#define CPCR_CDIR(x) (((x)&1)<<11) /* GPIO direction (PMR_PDREQLSEL = 1) */ ++#define CPCR_VS(x) (((x)&3)<<12) /* VS[2:1] output value */ ++#define CPCR_VSDIR(x) (((x)&3)<<14) /* VS[2:1] direction */ ++ ++#define PCTL0 (1 << 0) ++#define PCTL1 (1 << 1) ++#define PCTL2 (1 << 2) ++ ++#define CICR_ASRTMR1 (1 << 0) /* Timer 1 select for attribute read */ ++#define CICR_ASWTMR1 (1 << 1) /* Timer 1 select for attribute write */ ++#define CICR_IOSRTMR1 (1 << 2) /* Timer 1 select for IO read */ ++#define CICR_IOSWTMR1 (1 << 3) /* Timer 1 select for IO write */ ++#define CICR_MEMSRTMR1 (1 << 4) /* Timer 1 select for memory read */ ++#define CICR_MEMSWTMR1 (1 << 5) /* Timer 1 select for memory write */ ++#define CICR_AUTOIOSZ (1 << 6) /* Auto size I/O accesses */ ++#define CICR_CAW (1 << 7) /* Card access width */ ++#define CICR_IOMODE (1 << 8) /* IO mode select */ ++#define CICR_ENABLE (1 << 10) /* Card enable */ ++#define CICR_RESETOE (1 << 11) /* Card reset output enable */ ++#define CICR_RESET (1 << 12) /* Card reset */ ++ ++ ++#define RD_FAIL (1 << 14) ++#define WR_FAIL (1 << 13) ++#define IDLE (1 << 12) ++ ++#define FFOTHLD (1 << 11) ++#define PCM_RDYL (1 << 10) ++#define PCM_WP (1 << 9) ++#define PCTL (1 << 8) ++ ++#define PDREQ_L (1 << 6) ++#define PCM_VS2 (1 << 5) ++#define PCM_VS1 (1 << 4) ++ ++#define PCM_CD2 (1 << 3) ++#define PCM_CD1 (1 << 2) ++#define PCM_BVD2 (1 << 1) ++#define PCM_BVD1 (1 << 0) +diff -urN linux-2.4.26/drivers/pcmcia/cs.c linux-2.4.26-vrs1/drivers/pcmcia/cs.c +--- linux-2.4.26/drivers/pcmcia/cs.c 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/pcmcia/cs.c 2004-02-27 23:44:12.000000000 +0000 +@@ -3,7 +3,7 @@ + Kernel Card Services -- core services + + cs.c 1.271 2000/10/02 20:27:49 +- ++ + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of +@@ -28,7 +28,7 @@ + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. +- ++ + ======================================================================*/ + + #include +@@ -92,7 +92,7 @@ + MODULE_AUTHOR("David Hinds "); + MODULE_DESCRIPTION("Linux Kernel Card Services " CS_RELEASE + "\n options:" OPTIONS); +-MODULE_LICENSE("Dual MPL/GPL"); ++MODULE_LICENSE("Dual MPL/GPL"); + + #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i") + +@@ -123,7 +123,7 @@ + static const char *version = + "cs.c 1.279 2001/10/13 00:08:28 (David Hinds)"; + #endif +- ++ + /*====================================================================*/ + + socket_state_t dead_socket = { +@@ -299,7 +299,7 @@ + + Low-level PC Card interface drivers need to register with Card + Services using these calls. +- ++ + ======================================================================*/ + + static int setup_socket(socket_info_t *); +@@ -331,7 +331,7 @@ + s->use_bus_pm = use_bus_pm; + s->erase_busy.next = s->erase_busy.prev = &s->erase_busy; + spin_lock_init(&s->lock); +- ++ + for (i = 0; i < sockets; i++) + if (socket_table[i] == NULL) break; + socket_table[i] = s; +@@ -365,7 +365,7 @@ + for (ns = 0; ns < nsock; ns++) { + pcmcia_register_socket (ns, ss_entry, 0); + } +- ++ + return 0; + } /* register_ss_entry */ + +@@ -457,7 +457,7 @@ + static void shutdown_socket(socket_info_t *s) + { + client_t **c; +- ++ + DEBUG(1, "cs: shutdown_socket(%p)\n", s); + + /* Blank out the socket state */ +@@ -561,7 +561,7 @@ + have several causes: card insertion, a call to reset_socket, or + recovery from a suspend/resume cycle. Unreset_socket() sends + a CS event that matches the cause of the reset. +- ++ + ======================================================================*/ + + static void reset_socket(socket_info_t *s) +@@ -616,7 +616,7 @@ + s->state &= ~SOCKET_SETUP_PENDING; + } else { + send_event(s, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW); +- if (s->reset_handle) { ++ if (s->reset_handle) { + s->reset_handle->event_callback_args.info = NULL; + EVENT(s->reset_handle, CS_EVENT_RESET_COMPLETE, + CS_EVENT_PRI_LOW); +@@ -631,7 +631,7 @@ + valid clients. Parse_events() interprets the event bits from + a card status change report. Do_shutdown() handles the high + priority stuff associated with a card removal. +- ++ + ======================================================================*/ + + static int send_event(socket_info_t *s, event_t event, int priority) +@@ -641,7 +641,7 @@ + DEBUG(1, "cs: send_event(sock %d, event %d, pri %d)\n", + s->sock, event, priority); + ret = 0; +- for (; client; client = client->next) { ++ for (; client; client = client->next) { + if (client->state & (CLIENT_UNBOUND|CLIENT_STALE)) + continue; + if (client->EventMask & event) { +@@ -675,10 +675,17 @@ + static void parse_events(void *info, u_int events) + { + socket_info_t *s = info; ++ + if (events & SS_DETECT) { + int status; + + get_socket_status(s, &status); ++ ++ /* ++ * If our socket state indicates that a card is present and ++ * either the socket has not been suspended (for some reason) ++ * or the card has been removed, shut down the socket first. ++ */ + if ((s->state & SOCKET_PRESENT) && + (!(s->state & SOCKET_SUSPEND) || + !(status & SS_DETECT))) +@@ -716,7 +723,7 @@ + + This does not comply with the latest PC Card spec for handling + power management events. +- ++ + ======================================================================*/ + + void pcmcia_suspend_socket (socket_info_t *s) +@@ -773,7 +780,7 @@ + /*====================================================================== + + Special stuff for managing IO windows, because they are scarce. +- ++ + ======================================================================*/ + + static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base, +@@ -862,7 +869,7 @@ + Access_configuration_register() reads and writes configuration + registers in attribute memory. Memory window 0 is reserved for + this and the tuple reading services. +- ++ + ======================================================================*/ + + int pcmcia_access_configuration_register(client_handle_t handle, +@@ -872,7 +879,7 @@ + config_t *c; + int addr; + u_char val; +- ++ + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); +@@ -890,7 +897,7 @@ + return CS_CONFIGURATION_LOCKED; + + addr = (c->ConfigBase + reg->Offset) >> 1; +- ++ + switch (reg->Action) { + case CS_READ: + read_cis_mem(s, 1, addr, 1, &val); +@@ -913,7 +920,7 @@ + It is normally called by Driver Services after it has identified + a newly inserted card. An instance of that driver will then be + eligible to register as a client of this socket. +- ++ + ======================================================================*/ + + int pcmcia_bind_device(bind_req_t *req) +@@ -949,23 +956,23 @@ + region. It is normally called by Driver Services after it has + identified a memory device type. An instance of the corresponding + driver will then be able to register to control this region. +- ++ + ======================================================================*/ + + int pcmcia_bind_mtd(mtd_bind_t *req) + { + socket_info_t *s; + memory_handle_t region; +- ++ + if (CHECK_SOCKET(req->Socket)) + return CS_BAD_SOCKET; + s = SOCKET(req); +- ++ + if (req->Attributes & REGION_TYPE_AM) + region = s->a_region; + else + region = s->c_region; +- ++ + while (region) { + if (region->info.CardOffset == req->CardOffset) break; + region = region->info.next; +@@ -973,7 +980,7 @@ + if (!region || (region->mtd != NULL)) + return CS_BAD_OFFSET; + strncpy(region->dev_info, (char *)req->dev_info, DEV_NAME_LEN); +- ++ + DEBUG(1, "cs: bind_mtd(): attr 0x%x, offset 0x%x, dev %s\n", + req->Attributes, req->CardOffset, (char *)req->dev_info); + return CS_SUCCESS; +@@ -988,7 +995,7 @@ + memory_handle_t region; + u_long flags; + int i, sn; +- ++ + DEBUG(1, "cs: deregister_client(%p)\n", handle); + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; +@@ -1007,7 +1014,7 @@ + for (region = s->c_region; region; region = region->info.next) + if (region->mtd == handle) region->mtd = NULL; + } +- ++ + sn = handle->Socket; s = socket_table[sn]; + + if ((handle->state & CLIENT_STALE) || +@@ -1032,7 +1039,7 @@ + + if (--s->real_clients == 0) + register_callback(s, NULL, NULL); +- ++ + return CS_SUCCESS; + } /* deregister_client */ + +@@ -1043,7 +1050,7 @@ + { + socket_info_t *s; + config_t *c; +- ++ + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); +@@ -1055,7 +1062,7 @@ + return CS_BAD_ARGS; + } else + config->Function = handle->Function; +- ++ + #ifdef CONFIG_CARDBUS + if (s->state & SOCKET_CARDBUS) { + u_char fn = config->Function; +@@ -1076,16 +1083,16 @@ + return CS_SUCCESS; + } + #endif +- ++ + c = (s->config != NULL) ? &s->config[config->Function] : NULL; +- ++ + if ((c == NULL) || !(c->state & CONFIG_LOCKED)) { + config->Attributes = 0; + config->Vcc = s->socket.Vcc; + config->Vpp1 = config->Vpp2 = s->socket.Vpp; + return CS_SUCCESS; + } +- ++ + /* !!! This is a hack !!! */ + memcpy(&config->Attributes, &c->Attributes, sizeof(config_t)); + config->Attributes |= CONF_VALID_CLIENT; +@@ -1099,14 +1106,14 @@ + config->NumPorts2 = c->io.NumPorts2; + config->Attributes2 = c->io.Attributes2; + config->IOAddrLines = c->io.IOAddrLines; +- ++ + return CS_SUCCESS; + } /* get_configuration_info */ + + /*====================================================================== + + Return information about this version of Card Services. +- ++ + ======================================================================*/ + + int pcmcia_get_card_services_info(servinfo_t *info) +@@ -1124,7 +1131,7 @@ + + Note that get_first_client() *does* recognize the Socket field + in the request structure. +- ++ + ======================================================================*/ + + int pcmcia_get_first_client(client_handle_t *handle, client_req_t *req) +@@ -1239,7 +1246,7 @@ + + Get the current socket state bits. We don't support the latched + SocketState yet: I haven't seen any point for it. +- ++ + ======================================================================*/ + + int pcmcia_get_status(client_handle_t handle, cs_status_t *status) +@@ -1247,7 +1254,7 @@ + socket_info_t *s; + config_t *c; + int val; +- ++ + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); +@@ -1263,7 +1270,7 @@ + return CS_NO_CARD; + if (s->state & SOCKET_SETUP_PENDING) + status->CardState |= CS_EVENT_CARD_INSERTION; +- ++ + /* Get info from the PRR, if necessary */ + if (handle->Function == BIND_FN_ALL) { + if (status->Function && (status->Function >= s->functions)) +@@ -1309,7 +1316,7 @@ + /*====================================================================== + + Change the card address of an already open memory window. +- ++ + ======================================================================*/ + + int pcmcia_get_mem_page(window_handle_t win, memreq_t *req) +@@ -1338,7 +1345,7 @@ + /*====================================================================== + + Modify a locked socket configuration +- ++ + ======================================================================*/ + + int pcmcia_modify_configuration(client_handle_t handle, +@@ -1346,7 +1353,7 @@ + { + socket_info_t *s; + config_t *c; +- ++ + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); c = CONFIG(handle); +@@ -1354,7 +1361,7 @@ + return CS_NO_CARD; + if (!(c->state & CONFIG_LOCKED)) + return CS_CONFIGURATION_LOCKED; +- ++ + if (mod->Attributes & CONF_IRQ_CHANGE_VALID) { + if (mod->Attributes & CONF_ENABLE_IRQ) { + c->Attributes |= CONF_ENABLE_IRQ; +@@ -1406,7 +1413,7 @@ + win->ctl.flags |= MAP_USE_WAIT; + win->ctl.speed = req->AccessSpeed; + set_mem_map(win->sock, &win->ctl); +- ++ + return CS_SUCCESS; + } /* modify_window */ + +@@ -1416,7 +1423,7 @@ + caller with a socket. The driver must have already been bound + to a socket with bind_device() -- in fact, bind_device() + allocates the client structure that will be used. +- ++ + ======================================================================*/ + + int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) +@@ -1424,7 +1431,7 @@ + client_t *client; + socket_info_t *s; + socket_t ns; +- ++ + /* Look for unbound client with matching dev_info */ + client = NULL; + for (ns = 0; ns < sockets; ns++) { +@@ -1464,7 +1471,7 @@ + + if (s->state & SOCKET_CARDBUS) + client->state |= CLIENT_CARDBUS; +- ++ + if ((!(s->state & SOCKET_CARDBUS)) && (s->functions == 0) && + (client->Function != BIND_FN_ALL)) { + cistpl_longlink_mfc_t mfc; +@@ -1479,7 +1486,7 @@ + return CS_OUT_OF_RESOURCE; + memset(s->config, 0, sizeof(config_t) * s->functions); + } +- ++ + DEBUG(1, "cs: register_client(): client 0x%p, sock %d, dev %s\n", + client, client->Socket, client->dev_info); + if (client->EventMask & CS_EVENT_REGISTRATION_COMPLETE) +@@ -1501,13 +1508,13 @@ + pccard_io_map io = { 0, 0, 0, 0, 1 }; + socket_info_t *s; + int i; +- ++ + if (CHECK_HANDLE(handle) || + !(handle->state & CLIENT_CONFIG_LOCKED)) + return CS_BAD_HANDLE; + handle->state &= ~CLIENT_CONFIG_LOCKED; + s = SOCKET(handle); +- ++ + #ifdef CONFIG_CARDBUS + if (handle->state & CLIENT_CARDBUS) { + cb_disable(s); +@@ -1515,7 +1522,7 @@ + return CS_SUCCESS; + } + #endif +- ++ + if (!(handle->state & CLIENT_STALE)) { + config_t *c = CONFIG(handle); + if (--(s->lock_count) == 0) { +@@ -1536,7 +1543,7 @@ + } + c->state &= ~CONFIG_LOCKED; + } +- ++ + return CS_SUCCESS; + } /* release_configuration */ + +@@ -1547,25 +1554,25 @@ + the actual socket configuration, so if the client is "stale", we + don't bother checking the port ranges against the current socket + values. +- ++ + ======================================================================*/ + + int pcmcia_release_io(client_handle_t handle, io_req_t *req) + { + socket_info_t *s; +- ++ + if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IO_REQ)) + return CS_BAD_HANDLE; + handle->state &= ~CLIENT_IO_REQ; + s = SOCKET(handle); +- ++ + #ifdef CONFIG_CARDBUS + if (handle->state & CLIENT_CARDBUS) { + cb_release(s); + return CS_SUCCESS; + } + #endif +- ++ + if (!(handle->state & CLIENT_STALE)) { + config_t *c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) +@@ -1581,7 +1588,7 @@ + release_io_space(s, req->BasePort1, req->NumPorts1); + if (req->NumPorts2) + release_io_space(s, req->BasePort2, req->NumPorts2); +- ++ + return CS_SUCCESS; + } /* release_io */ + +@@ -1594,7 +1601,7 @@ + return CS_BAD_HANDLE; + handle->state &= ~CLIENT_IRQ_REQ; + s = SOCKET(handle); +- ++ + if (!(handle->state & CLIENT_STALE)) { + config_t *c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) +@@ -1608,16 +1615,16 @@ + s->irq.AssignedIRQ = 0; + } + } +- ++ + if (req->Attributes & IRQ_HANDLE_PRESENT) { + bus_free_irq(s->cap.bus, req->AssignedIRQ, req->Instance); + } + +-#ifdef CONFIG_ISA ++#ifdef CONFIG_PCMCIA_PROBE + if (req->AssignedIRQ != s->cap.pci_irq) + undo_irq(req->Attributes, req->AssignedIRQ); + #endif +- ++ + return CS_SUCCESS; + } /* cs_release_irq */ + +@@ -1626,7 +1633,7 @@ + int pcmcia_release_window(window_handle_t win) + { + socket_info_t *s; +- ++ + if ((win == NULL) || (win->magic != WINDOW_MAGIC)) + return CS_BAD_HANDLE; + s = win->sock; +@@ -1640,11 +1647,11 @@ + + /* Release system memory */ + if(!(s->cap.features & SS_CAP_STATIC_MAP)) +- release_mem_region(win->base, win->size); ++ release_mem_resource(win->base, win->size); + win->handle->state &= ~CLIENT_WIN_REQ(win->index); + + win->magic = 0; +- ++ + return CS_SUCCESS; + } /* release_window */ + +@@ -1658,13 +1665,13 @@ + socket_info_t *s; + config_t *c; + pccard_io_map iomap; +- ++ + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + i = handle->Socket; s = socket_table[i]; + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; +- ++ + #ifdef CONFIG_CARDBUS + if (handle->state & CLIENT_CARDBUS) { + if (!(req->IntType & INT_CARDBUS)) +@@ -1677,7 +1684,7 @@ + return CS_SUCCESS; + } + #endif +- ++ + if (req->IntType & INT_CARDBUS) + return CS_UNSUPPORTED_MODE; + c = CONFIG(handle); +@@ -1692,9 +1699,9 @@ + s->socket.Vpp = req->Vpp1; + if (set_socket(s, &s->socket)) + return CS_BAD_VPP; +- ++ + c->Vcc = req->Vcc; c->Vpp1 = c->Vpp2 = req->Vpp1; +- ++ + /* Pick memory or I/O card, DMA mode, interrupt */ + c->IntType = req->IntType; + c->Attributes = req->Attributes; +@@ -1712,7 +1719,7 @@ + s->socket.io_irq = 0; + set_socket(s, &s->socket); + s->lock_count++; +- ++ + /* Set up CIS configuration registers */ + base = c->ConfigBase = req->ConfigBase; + c->Present = c->CardValues = req->Present; +@@ -1757,7 +1764,7 @@ + u_char b = c->io.NumPorts1 + c->io.NumPorts2 - 1; + write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b); + } +- ++ + /* Configure I/O windows */ + if (c->state & CONFIG_IO_REQ) { + iomap.speed = io_speed; +@@ -1779,24 +1786,24 @@ + s->io[i].Config++; + } + } +- ++ + c->state |= CONFIG_LOCKED; + handle->state |= CLIENT_CONFIG_LOCKED; + return CS_SUCCESS; + } /* request_configuration */ + + /*====================================================================== +- ++ + Request_io() reserves ranges of port addresses for a socket. + I have not implemented range sharing or alias addressing. +- ++ + ======================================================================*/ + + int pcmcia_request_io(client_handle_t handle, io_req_t *req) + { + socket_info_t *s; + config_t *c; +- ++ + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); +@@ -1855,7 +1862,7 @@ + hooked, we don't guarantee that an irq will still be available + when the configuration is locked. Now that I think about it, + there might be a way to fix this using a dummy handler. +- ++ + ======================================================================*/ + + int pcmcia_request_irq(client_handle_t handle, irq_req_t *req) +@@ -1863,7 +1870,7 @@ + socket_info_t *s; + config_t *c; + int ret = CS_IN_USE, irq = 0; +- ++ + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); +@@ -1875,7 +1882,7 @@ + if (c->state & CONFIG_IRQ_REQ) + return CS_IN_USE; + +-#ifdef CONFIG_ISA ++#ifdef CONFIG_PCMCIA_PROBE + if (s->irq.AssignedIRQ != 0) { + /* If the interrupt is already assigned, it must match */ + irq = s->irq.AssignedIRQ; +@@ -1909,7 +1916,7 @@ + + if (req->Attributes & IRQ_HANDLE_PRESENT) { + if (bus_request_irq(s->cap.bus, irq, req->Handler, +- ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || ++ ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || + (s->functions > 1) || + (irq == s->cap.pci_irq)) ? SA_SHIRQ : 0, + handle->dev_info, req->Instance)) +@@ -1919,7 +1926,7 @@ + c->irq.Attributes = req->Attributes; + s->irq.AssignedIRQ = req->AssignedIRQ = irq; + s->irq.Config++; +- ++ + c->state |= CONFIG_IRQ_REQ; + handle->state |= CLIENT_IRQ_REQ; + return CS_SUCCESS; +@@ -1938,7 +1945,7 @@ + window_t *win; + u_long align; + int w; +- ++ + if (CHECK_HANDLE(*handle)) + return CS_BAD_HANDLE; + s = SOCKET(*handle); +@@ -2005,7 +2012,7 @@ + /* Return window handle */ + req->Base = win->ctl.sys_start; + *wh = win; +- ++ + return CS_SUCCESS; + } /* request_window */ + +@@ -2014,14 +2021,14 @@ + I'm not sure which "reset" function this is supposed to use, + but for now, it uses the low-level interface's reset, not the + CIS register. +- ++ + ======================================================================*/ + + int pcmcia_reset_card(client_handle_t handle, client_req_t *req) + { + int i, ret; + socket_info_t *s; +- ++ + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + i = handle->Socket; s = socket_table[i]; +@@ -2049,14 +2056,14 @@ + + These shut down or wake up a socket. They are sort of user + initiated versions of the APM suspend and resume actions. +- ++ + ======================================================================*/ + + int pcmcia_suspend_card(client_handle_t handle, client_req_t *req) + { + int i; + socket_info_t *s; +- ++ + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + i = handle->Socket; s = socket_table[i]; +@@ -2077,7 +2084,7 @@ + { + int i; + socket_info_t *s; +- ++ + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + i = handle->Socket; s = socket_table[i]; +@@ -2095,7 +2102,7 @@ + /*====================================================================== + + These handle user requests to eject or insert a card. +- ++ + ======================================================================*/ + + int pcmcia_eject_card(client_handle_t handle, client_req_t *req) +@@ -2103,7 +2110,7 @@ + int i, ret; + socket_info_t *s; + u_long flags; +- ++ + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + i = handle->Socket; s = socket_table[i]; +@@ -2119,9 +2126,9 @@ + spin_lock_irqsave(&s->lock, flags); + do_shutdown(s); + spin_unlock_irqrestore(&s->lock, flags); +- ++ + return CS_SUCCESS; +- ++ + } /* eject_card */ + + int pcmcia_insert_card(client_handle_t handle, client_req_t *req) +@@ -2129,7 +2136,7 @@ + int i, status; + socket_info_t *s; + u_long flags; +- ++ + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + i = handle->Socket; s = socket_table[i]; +@@ -2157,7 +2164,7 @@ + + Maybe this should send a CS_EVENT_CARD_INSERTION event if we + haven't sent one to this client yet? +- ++ + ======================================================================*/ + + int pcmcia_set_event_mask(client_handle_t handle, eventmask_t *mask) +@@ -2189,7 +2196,7 @@ + printk(KERN_NOTICE); + else + printk(KERN_NOTICE "%s: ", handle->dev_info); +- ++ + for (i = 0; i < SERVICE_COUNT; i++) + if (service_table[i].key == err->func) break; + if (i < SERVICE_COUNT) +@@ -2347,13 +2354,13 @@ + default: + return CS_UNSUPPORTED_FUNCTION; break; + } +- ++ + } /* CardServices */ + + /*====================================================================== + + OS-specific module glue goes here +- ++ + ======================================================================*/ + /* in alpha order */ + EXPORT_SYMBOL(pcmcia_access_configuration_register); +@@ -2450,4 +2457,3 @@ + module_exit(exit_pcmcia_cs); + + /*====================================================================*/ +- +diff -urN linux-2.4.26/drivers/pcmcia/ds.c linux-2.4.26-vrs1/drivers/pcmcia/ds.c +--- linux-2.4.26/drivers/pcmcia/ds.c 2001-11-12 17:39:01.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/pcmcia/ds.c 2004-01-14 21:32:26.000000000 +0000 +@@ -55,6 +55,7 @@ + #include + #include + #include ++#include + + /*====================================================================*/ + +@@ -880,6 +881,8 @@ + EXPORT_SYMBOL(register_pccard_driver); + EXPORT_SYMBOL(unregister_pccard_driver); + ++static devfs_handle_t devfs_handle; ++ + /*====================================================================*/ + + int __init init_pcmcia_ds(void) +@@ -957,8 +960,13 @@ + if (i == -EBUSY) + printk(KERN_NOTICE "unable to find a free device # for " + "Driver Services\n"); +- else ++ else { + major_dev = i; ++ devfs_handle = devfs_register(NULL, "pcmcia", DEVFS_FL_DEFAULT, ++ major_dev, 0, ++ S_IFCHR | S_IRUSR | S_IWUSR, ++ &ds_fops, NULL); ++ } + + #ifdef CONFIG_PROC_FS + if (proc_pccard) +@@ -983,7 +991,9 @@ + remove_proc_entry("drivers", proc_pccard); + #endif + if (major_dev != -1) +- unregister_chrdev(major_dev, "pcmcia"); ++ devfs_unregister_chrdev(major_dev, "pcmcia"); ++ devfs_unregister(devfs_handle); ++ + for (i = 0; i < sockets; i++) + pcmcia_deregister_client(socket_table[i].handle); + sockets = 0; +diff -urN linux-2.4.26/drivers/pcmcia/i82365.c linux-2.4.26-vrs1/drivers/pcmcia/i82365.c +--- linux-2.4.26/drivers/pcmcia/i82365.c 2003-11-28 18:26:20.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/pcmcia/i82365.c 2004-02-22 14:44:10.000000000 +0000 +@@ -28,7 +28,7 @@ + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. +- ++ + ======================================================================*/ + + #include +@@ -65,6 +65,15 @@ + #include "ricoh.h" + #include "o2micro.h" + ++#ifdef CONFIG_ARCH_EBSA110 ++#define I365_MASK (1 << 6) ++#define SOCKIRQ2REG(sock,irq) ((irq) ? ((sock) ? 3 : 4) : 0) ++#define REG2SOCKIRQ(sock,reg) (6) ++#else ++#define SOCKIRQ2REG(sock,irq) (irq) ++#define REG2SOCKIRQ(sock,reg) (reg) ++#endif ++ + #ifdef PCMCIA_DEBUG + static int pc_debug = PCMCIA_DEBUG; + MODULE_PARM(pc_debug, "i"); +@@ -173,13 +182,15 @@ + } socket_info_t; + + /* Where we keep track of our sockets... */ +-static int sockets = 0; +-static socket_info_t socket[8] = { +- { 0, }, /* ... */ +-}; ++static int sockets /* = 0 */; ++static socket_info_t socket[8] /* = { ++ { 0, }, ++} */; + + /* Default ISA interrupt mask */ ++#ifndef I365_MASK + #define I365_MASK 0xdeb8 /* irq 15,14,12,11,10,9,7,5,4,3 */ ++#endif + + static int grab_irq; + static spinlock_t isa_lock = SPIN_LOCK_UNLOCKED; +@@ -303,7 +314,7 @@ + + The VIA controllers also use these routines, as they are mostly + Cirrus lookalikes, without the timing registers. +- ++ + ======================================================================*/ + + #define flip(v,b,f) (v = ((f)<0) ? v : ((f) ? ((v)|(b)) : ((v)&(~b)))) +@@ -389,7 +400,7 @@ + Code to save and restore global state information for Vadem VG468 + and VG469 controllers, and to set and report global configuration + options. +- ++ + ======================================================================*/ + + static void vg46x_get_state(u_short s) +@@ -411,7 +422,7 @@ + static u_int __init vg46x_set_opts(u_short s, char *buf) + { + vg46x_state_t *p = &socket[s].state.vg46x; +- ++ + flip(p->ctl, VG468_CTL_ASYNC, async_clock); + flip(p->ema, VG469_MODE_CABLE, cable_mode); + if (p->ctl & VG468_CTL_ASYNC) +@@ -436,7 +447,7 @@ + /*====================================================================== + + Generic routines to get and set controller options +- ++ + ======================================================================*/ + + static void get_bridge_state(u_short s) +@@ -489,7 +500,7 @@ + /*====================================================================== + + Interrupt testing code, for ISA and PCI interrupts +- ++ + ======================================================================*/ + + static volatile u_int irq_hits; +@@ -517,7 +528,7 @@ + } + + /* Generate one interrupt */ +- i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (irq << 4)); ++ i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (SOCKIRQ2REG(sock, irq) << 4)); + i365_bset(sock, I365_GENCTL, I365_CTL_SW_IRQ); + udelay(1000); + +@@ -526,7 +537,7 @@ + /* mask all interrupts */ + i365_set(sock, I365_CSCINT, 0); + DEBUG(2, " hits = %d\n", irq_hits); +- ++ + return (irq_hits != 1); + } + +@@ -540,7 +551,7 @@ + /* Don't probe level-triggered interrupts -- reserved for PCI */ + mask0 &= ~(inb(PIC) | (inb(PIC+1) << 8)); + #endif +- ++ + if (do_scan) { + set_bridge_state(sock); + i365_set(sock, I365_CSCINT, 0); +@@ -551,7 +562,7 @@ + if ((mask1 & (1 << i)) && (test_irq(sock, i) != 0)) + mask1 ^= (1 << i); + } +- ++ + printk(KERN_INFO " ISA irqs ("); + if (mask1) { + printk("scanned"); +@@ -565,12 +576,12 @@ + if (!cs_irq && (poll_interval == 0)) poll_interval = HZ; + } + printk(") = "); +- ++ + for (i = 0; i < 16; i++) + if (mask1 & (1<ioaddr > 0) request_region(t->ioaddr, 2, "i82365"); +- ++ + if (base == 0) printk("\n"); + printk(KERN_INFO " %s", pcic[type].name); + printk(" ISA-to-PCMCIA at port %#x ofs 0x%02x", +@@ -713,7 +724,7 @@ + mask &= I365_MASK & set_bridge_opts(base, ns); + /* Scan for ISA interrupts */ + mask = isa_scan(base, mask); +- ++ + /* Poll if only two interrupts available */ + if (!poll_interval) { + u_int tmp = (mask & 0xff20); +@@ -735,15 +746,15 @@ + printk(" status change on irq %d\n", cs_irq); + } + } +- ++ + if (!isa_irq) { + if (poll_interval == 0) + poll_interval = HZ; + printk(" polling interval = %d ms\n", + poll_interval * 1000 / HZ); +- ++ + } +- ++ + /* Update socket interrupt information, capabilities */ + for (i = 0; i < ns; i++) { + t[i].cap.features |= SS_CAP_PCCARD; +@@ -866,12 +877,12 @@ + events = pending_events[i]; + pending_events[i] = 0; + spin_unlock_irq(&pending_event_lock); +- /* +- SS_DETECT events need a small delay here. The reason for this is that ++ /* ++ SS_DETECT events need a small delay here. The reason for this is that + the "is there a card" electronics need time to see the card after the +- "we have a card coming in" electronics have seen it. ++ "we have a card coming in" electronics have seen it. + */ +- if (events & SS_DETECT) ++ if (events & SS_DETECT) + mdelay(4); + if (socket[i].handler) + socket[i].handler(socket[i].info, events); +@@ -890,7 +901,7 @@ + int i, j, csc; + u_int events, active; + u_long flags = 0; +- ++ + DEBUG(4, "i82365: pcic_interrupt(%d)\n", irq); + + for (j = 0; j < 20; j++) { +@@ -906,20 +917,20 @@ + continue; + } + events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0; +- +- ++ ++ + /* Several sockets will send multiple "new card detected" +- events in rapid succession. However, the rest of the pcmcia expects ++ events in rapid succession. However, the rest of the pcmcia expects + only one such event. We just ignore these events by having a + timeout */ + + if (events) { +- if ((jiffies - last_detect_jiffies)<(HZ/20)) ++ if ((jiffies - last_detect_jiffies)<(HZ/20)) + events = 0; + last_detect_jiffies = jiffies; +- ++ + } +- ++ + if (i365_get(i, I365_INTCTL) & I365_PC_IOCARD) + events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0; + else { +@@ -980,11 +991,11 @@ + static int i365_get_status(u_short sock, u_int *value) + { + u_int status; +- ++ + status = i365_get(sock, I365_STATUS); + *value = ((status & I365_CS_DETECT) == I365_CS_DETECT) + ? SS_DETECT : 0; +- ++ + if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) + *value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG; + else { +@@ -1005,7 +1016,7 @@ + *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD; + } + } +- ++ + DEBUG(1, "i82365: GetStatus(%d) = %#4.4x\n", sock, *value); + return 0; + } /* i365_get_status */ +@@ -1016,7 +1027,7 @@ + { + socket_info_t *t = &socket[sock]; + u_char reg, vcc, vpp; +- ++ + reg = i365_get(sock, I365_POWER); + state->flags = (reg & I365_PWR_AUTO) ? SS_PWR_AUTO : 0; + state->flags |= (reg & I365_PWR_OUT) ? SS_OUTPUT_ENA : 0; +@@ -1057,14 +1068,14 @@ + reg = i365_get(sock, I365_INTCTL); + state->flags |= (reg & I365_PC_RESET) ? 0 : SS_RESET; + if (reg & I365_PC_IOCARD) state->flags |= SS_IOCARD; +- state->io_irq = reg & I365_IRQ_MASK; +- ++ state->io_irq = REG2SOCKIRQ(sock, reg & I365_IRQ_MASK); ++ + /* speaker control */ + if (t->flags & IS_CIRRUS) { + if (i365_get(sock, PD67_MISC_CTL_1) & PD67_MC1_SPKR_ENA) + state->flags |= SS_SPKR_ENA; + } +- ++ + /* Card status change mask */ + reg = i365_get(sock, I365_CSCINT); + state->csc_mask = (reg & I365_CSC_DETECT) ? SS_DETECT : 0; +@@ -1075,7 +1086,7 @@ + state->csc_mask |= (reg & I365_CSC_BVD2) ? SS_BATWARN : 0; + state->csc_mask |= (reg & I365_CSC_READY) ? SS_READY : 0; + } +- ++ + DEBUG(1, "i82365: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, " + "io_irq %d, csc_mask %#2.2x\n", sock, state->flags, + state->Vcc, state->Vpp, state->io_irq, state->csc_mask); +@@ -1088,21 +1099,21 @@ + { + socket_info_t *t = &socket[sock]; + u_char reg; +- ++ + DEBUG(1, "i82365: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " + "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags, + state->Vcc, state->Vpp, state->io_irq, state->csc_mask); +- ++ + /* First set global controller options */ + set_bridge_state(sock); +- ++ + /* IO card, RESET flag, IO interrupt */ + reg = t->intr; +- reg |= state->io_irq; ++ reg |= SOCKIRQ2REG(sock, state->io_irq); + reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET; + reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0; + i365_set(sock, I365_INTCTL, reg); +- ++ + reg = I365_PWR_NORESET; + if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO; + if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT; +@@ -1165,7 +1176,7 @@ + default: return -EINVAL; + } + } +- ++ + if (reg != i365_get(sock, I365_POWER)) + i365_set(sock, I365_POWER, reg); + +@@ -1175,9 +1186,9 @@ + i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA, + state->flags & SS_SPKR_ENA); + } +- ++ + /* Card status change interrupt mask */ +- reg = t->cs_irq << 4; ++ reg = SOCKIRQ2REG(sock, t->cs_irq) << 4; + if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT; + if (state->flags & SS_IOCARD) { + if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG; +@@ -1188,7 +1199,7 @@ + } + i365_set(sock, I365_CSCINT, reg); + i365_get(sock, I365_CSC); +- ++ + return 0; + } /* i365_set_socket */ + +@@ -1197,7 +1208,7 @@ + static int i365_get_io_map(u_short sock, struct pccard_io_map *io) + { + u_char map, ioctl, addr; +- ++ + map = io->map; + if (map > 1) return -EINVAL; + io->start = i365_get_pair(sock, I365_IO(map)+I365_W_START); +@@ -1220,7 +1231,7 @@ + static int i365_set_io_map(u_short sock, struct pccard_io_map *io) + { + u_char map, ioctl; +- ++ + DEBUG(1, "i82365: SetIOMap(%d, %d, %#2.2x, %d ns, " + "%#4.4x-%#4.4x)\n", sock, io->map, io->flags, + io->speed, io->start, io->stop); +@@ -1250,30 +1261,30 @@ + { + u_short base, i; + u_char map, addr; +- ++ + map = mem->map; + if (map > 4) return -EINVAL; + addr = i365_get(sock, I365_ADDRWIN); + mem->flags = (addr & I365_ENA_MEM(map)) ? MAP_ACTIVE : 0; + base = I365_MEM(map); +- ++ + i = i365_get_pair(sock, base+I365_W_START); + mem->flags |= (i & I365_MEM_16BIT) ? MAP_16BIT : 0; + mem->flags |= (i & I365_MEM_0WS) ? MAP_0WS : 0; + mem->sys_start = ((u_long)(i & 0x0fff) << 12); +- ++ + i = i365_get_pair(sock, base+I365_W_STOP); + mem->speed = (i & I365_MEM_WS0) ? 1 : 0; + mem->speed += (i & I365_MEM_WS1) ? 2 : 0; + mem->speed = to_ns(mem->speed); + mem->sys_stop = ((u_long)(i & 0x0fff) << 12) + 0x0fff; +- ++ + i = i365_get_pair(sock, base+I365_W_OFF); + mem->flags |= (i & I365_MEM_WRPROT) ? MAP_WRPROT : 0; + mem->flags |= (i & I365_MEM_REG) ? MAP_ATTRIB : 0; + mem->card_start = ((u_int)(i & 0x3fff) << 12) + mem->sys_start; + mem->card_start &= 0x3ffffff; +- ++ + DEBUG(1, "i82365: GetMemMap(%d, %d) = %#2.2x, %d ns, %#5.5lx-%#5." + "5lx, %#5.5x\n", sock, mem->map, mem->flags, mem->speed, + mem->sys_start, mem->sys_stop, mem->card_start); +@@ -1281,12 +1292,12 @@ + } /* i365_get_mem_map */ + + /*====================================================================*/ +- ++ + static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem) + { + u_short base, i; + u_char map; +- ++ + DEBUG(1, "i82365: SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5" + "lx, %#5.5x)\n", sock, mem->map, mem->flags, mem->speed, + mem->sys_start, mem->sys_stop, mem->card_start); +@@ -1297,17 +1308,17 @@ + return -EINVAL; + if ((mem->sys_start > 0xffffff) || (mem->sys_stop > 0xffffff)) + return -EINVAL; +- ++ + /* Turn off the window before changing anything */ + if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map)) + i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map)); +- ++ + base = I365_MEM(map); + i = (mem->sys_start >> 12) & 0x0fff; + if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT; + if (mem->flags & MAP_0WS) i |= I365_MEM_0WS; + i365_set_pair(sock, base+I365_W_START, i); +- ++ + i = (mem->sys_stop >> 12) & 0x0fff; + switch (to_cycles(mem->speed)) { + case 0: break; +@@ -1316,12 +1327,12 @@ + default: i |= I365_MEM_WS1 | I365_MEM_WS0; break; + } + i365_set_pair(sock, base+I365_W_STOP, i); +- ++ + i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff; + if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT; + if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG; + i365_set_pair(sock, base+I365_W_OFF, i); +- ++ + /* Turn on the window if necessary */ + if (mem->flags & MAP_ACTIVE) + i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map)); +@@ -1332,7 +1343,7 @@ + + Routines for accessing socket information and register dumps via + /proc/bus/pccard/... +- ++ + ======================================================================*/ + + #ifdef CONFIG_PROC_FS +@@ -1353,7 +1364,7 @@ + u_short sock = (socket_info_t *)data - socket; + char *p = buf; + int i, top; +- ++ + u_long flags = 0; + ISA_LOCK(sock, flags); + top = 0x40; +@@ -1399,7 +1410,7 @@ + + /*====================================================================*/ + +-/* this is horribly ugly... proper locking needs to be done here at ++/* this is horribly ugly... proper locking needs to be done here at + * some time... */ + #define LOCKED(x) do { \ + int retval; \ +@@ -1532,7 +1543,7 @@ + /* Set up interrupt handler(s) */ + if (grab_irq != 0) + request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt); +- ++ + if (register_ss_entry(sockets, &pcic_operations) != 0) + printk(KERN_NOTICE "i82365: register_ss_entry() failed\n"); + +@@ -1544,9 +1555,9 @@ + poll_timer.expires = jiffies + poll_interval; + add_timer(&poll_timer); + } +- ++ + return 0; +- ++ + } /* init_i82365 */ + + static void __exit exit_i82365(void) +diff -urN linux-2.4.26/drivers/pcmcia/rsrc_mgr.c linux-2.4.26-vrs1/drivers/pcmcia/rsrc_mgr.c +--- linux-2.4.26/drivers/pcmcia/rsrc_mgr.c 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/pcmcia/rsrc_mgr.c 2004-02-23 13:36:32.000000000 +0000 +@@ -28,7 +28,7 @@ + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. +- ++ + ======================================================================*/ + + #define __NO_VERSION__ +@@ -55,6 +55,10 @@ + #include + #include "cs_internal.h" + ++#ifndef ISAMEM_PHYS ++#define ISAMEM_PHYS 0 ++#endif ++ + /*====================================================================*/ + + /* Parameters that can be set with 'insmod' */ +@@ -62,7 +66,7 @@ + #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i") + + INT_MODULE_PARM(probe_mem, 1); /* memory probe? */ +-#ifdef CONFIG_ISA ++#ifdef CONFIG_PCMCIA_PROBE + INT_MODULE_PARM(probe_io, 1); /* IO port probe? */ + INT_MODULE_PARM(mem_limit, 0x10000); + #endif +@@ -85,7 +89,7 @@ + /* IO port resource database */ + static resource_map_t io_db = { 0, 0, &io_db }; + +-#ifdef CONFIG_ISA ++#ifdef CONFIG_PCMCIA_PROBE + + typedef struct irq_info_t { + u_int Attributes; +@@ -133,6 +137,7 @@ + static inline int check_mem_resource(unsigned long b, unsigned long n, + struct pci_dev *dev) + { ++ b += ISAMEM_PHYS; + return check_resource(resource_parent(b, n, IORESOURCE_MEM, dev), b, n); + } + +@@ -169,10 +174,15 @@ + static int request_mem_resource(unsigned long b, unsigned long n, + char *name, struct pci_dev *dev) + { +- struct resource *res = make_resource(b, n, IORESOURCE_MEM, name); +- struct resource *pr = resource_parent(b, n, IORESOURCE_MEM, dev); ++ struct resource *res; ++ struct resource *pr; + int err = -ENOMEM; + ++ b += ISAMEM_PHYS; ++ ++ res = make_resource(b, n, IORESOURCE_MEM, name); ++ pr = resource_parent(b, n, IORESOURCE_MEM, dev); ++ + if (res) { + err = request_resource(pr, res); + if (err) +@@ -181,10 +191,16 @@ + return err; + } + ++void release_mem_resource(unsigned long b, unsigned long n) ++{ ++ b += ISAMEM_PHYS; ++ release_mem_region(b, n); ++} ++ + /*====================================================================== + + These manage the internal databases of available resources. +- ++ + ======================================================================*/ + + static int add_interval(resource_map_t *map, u_long base, u_long num) +@@ -248,25 +264,25 @@ + + These routines examine a region of IO or memory addresses to + determine what ranges might be genuinely available. +- ++ + ======================================================================*/ + +-#ifdef CONFIG_ISA ++#ifdef CONFIG_PCMCIA_PROBE + static void do_io_probe(ioaddr_t base, ioaddr_t num) + { +- ++ + ioaddr_t i, j, bad, any; + u_char *b, hole, most; +- ++ + printk(KERN_INFO "cs: IO port probe 0x%04x-0x%04x:", + base, base+num-1); +- ++ + /* First, what does a floating port look like? */ + b = kmalloc(256, GFP_KERNEL); + if (!b) { + printk(KERN_ERR "do_io_probe: unable to kmalloc 256 bytes"); + return; +- } ++ } + memset(b, 0, 256); + for (i = base, most = 0; i < base+num; i += 8) { + if (check_io_resource(i, 8, NULL)) +@@ -308,7 +324,7 @@ + printk(" %#04x-%#04x", bad, i-1); + } + } +- ++ + printk(any ? "\n" : " clean.\n"); + } + #endif +@@ -318,7 +334,7 @@ + The memory probe. If the memory list includes a 64K-aligned block + below 1MB, we probe in 64K chunks, and as soon as we accumulate at + least mem_limit free space, we quit. +- ++ + ======================================================================*/ + + static int do_mem_probe(u_long base, u_long num, +@@ -332,7 +348,7 @@ + bad = fail = 0; + step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff); + for (i = j = base; i < base+num; i = j + step) { +- if (!fail) { ++ if (!fail) { + for (j = i; j < base+num; j += step) + if ((check_mem_resource(j, step, s->cap.cb_dev) == 0) && + is_valid(j)) +@@ -356,7 +372,7 @@ + return (num - bad); + } + +-#ifdef CONFIG_ISA ++#ifdef CONFIG_PCMCIA_PROBE + + static u_long inv_probe(int (*is_valid)(u_long), + int (*do_cksum)(u_long), +@@ -383,7 +399,7 @@ + static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 }; + static int hi = 0, lo = 0; + u_long b, i, ok = 0; +- ++ + if (!probe_mem) return; + /* We do up to four passes through the list */ + if (!force_low) { +@@ -414,14 +430,14 @@ + } + } + +-#else /* CONFIG_ISA */ ++#else /* CONFIG_PCMCIA_PROBE */ + + void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long), + int force_low, socket_info_t *s) + { + resource_map_t *m, *n; + static int done = 0; +- ++ + if (!probe_mem || done++) + return; + +@@ -432,7 +448,7 @@ + } + } + +-#endif /* CONFIG_ISA */ ++#endif /* CONFIG_PCMCIA_PROBE */ + + /*====================================================================== + +@@ -444,7 +460,7 @@ + should be a power of two, greater than or equal to 'num'. A value + of 0 means that all bits of *base are significant. *base should + also be strictly less than 'align'. +- ++ + ======================================================================*/ + + int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align, +@@ -452,7 +468,7 @@ + { + ioaddr_t try; + resource_map_t *m; +- ++ + for (m = io_db.next; m != &io_db; m = m->next) { + try = (m->base & ~(align-1)) + *base; + for (try = (try >= m->base) ? try : try+align; +@@ -500,10 +516,10 @@ + This checks to see if an interrupt is available, with support + for interrupt sharing. We don't support reserving interrupts + yet. If the interrupt is available, we allocate it. +- ++ + ======================================================================*/ + +-#ifdef CONFIG_ISA ++#ifdef CONFIG_PCMCIA_PROBE + + static void fake_irq(int i, void *d, struct pt_regs *r) { } + static inline int check_irq(int irq) +@@ -570,7 +586,7 @@ + + /*====================================================================*/ + +-#ifdef CONFIG_ISA ++#ifdef CONFIG_PCMCIA_PROBE + + void undo_irq(u_int Attributes, int irq) + { +@@ -600,7 +616,7 @@ + + The various adjust_* calls form the external interface to the + resource database. +- ++ + ======================================================================*/ + + static int adjust_memory(adjust_t *adj) +@@ -632,7 +648,7 @@ + default: + ret = CS_UNSUPPORTED_FUNCTION; + } +- ++ + return ret; + } + +@@ -641,7 +657,7 @@ + static int adjust_io(adjust_t *adj) + { + int base, num; +- ++ + base = adj->resource.io.BasePort; + num = adj->resource.io.NumPorts; + if ((base < 0) || (base > 0xffff)) +@@ -653,7 +669,7 @@ + case ADD_MANAGED_RESOURCE: + if (add_interval(&io_db, base, num) != 0) + return CS_IN_USE; +-#ifdef CONFIG_ISA ++#ifdef CONFIG_PCMCIA_PROBE + if (probe_io) + do_io_probe(base, num); + #endif +@@ -673,15 +689,15 @@ + + static int adjust_irq(adjust_t *adj) + { +-#ifdef CONFIG_ISA ++#ifdef CONFIG_PCMCIA_PROBE + int irq; + irq_info_t *info; +- ++ + irq = adj->resource.irq.IRQ; + if ((irq < 0) || (irq > 15)) + return CS_BAD_IRQ; + info = &irq_table[irq]; +- ++ + switch (adj->Action) { + case ADD_MANAGED_RESOURCE: + if (info->Attributes & RES_REMOVED) +@@ -716,7 +732,7 @@ + { + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; +- ++ + switch (adj->Resource) { + case RES_MEMORY_RANGE: + return adjust_memory(adj); +@@ -736,7 +752,7 @@ + void release_resource_db(void) + { + resource_map_t *p, *q; +- ++ + for (p = mem_db.next; p != &mem_db; p = q) { + q = p->next; + kfree(p); +diff -urN linux-2.4.26/drivers/pcmcia/sa1100.h linux-2.4.26-vrs1/drivers/pcmcia/sa1100.h +--- linux-2.4.26/drivers/pcmcia/sa1100.h 2002-08-03 01:39:44.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100.h 2004-01-14 21:32:26.000000000 +0000 +@@ -204,7 +204,9 @@ + extern struct pcmcia_low_level flexanet_pcmcia_ops; + extern struct pcmcia_low_level simpad_pcmcia_ops; + extern struct pcmcia_low_level graphicsmaster_pcmcia_ops; ++extern struct pcmcia_low_level adsagc_pcmcia_ops; + extern struct pcmcia_low_level adsbitsy_pcmcia_ops; ++extern struct pcmcia_low_level adsbitsyplus_pcmcia_ops; + extern struct pcmcia_low_level stork_pcmcia_ops; + extern struct pcmcia_low_level badge4_pcmcia_ops; + +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_adsbitsy.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_adsbitsy.c +--- linux-2.4.26/drivers/pcmcia/sa1100_adsbitsy.c 2002-08-03 01:39:44.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_adsbitsy.c 2004-01-14 21:32:26.000000000 +0000 +@@ -11,28 +11,156 @@ + */ + #include + #include ++#include + + #include ++#include ++#include + + #include "sa1100_generic.h" + #include "sa1111_generic.h" + ++int adsbitsy_smc91111_present(void); ++ ++#ifndef CONFIG_SMC91111 ++#define adsbitsy_smc91111_present() 0 ++#endif ++ ++static struct irqs { ++ int irq; ++ const char *str; ++} irqs[] = { ++ { S0_CD_VALID, "SA1111 PCMCIA card detect" }, ++ { S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1" }, ++ { S1_CD_VALID, "SA1111 CF card detect" }, ++ { S1_BVD1_STSCHG, "SA1111 CF BVD1" }, ++}; ++ + static int adsbitsy_pcmcia_init(struct pcmcia_init *init) + { +- /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ +- PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); ++ int ret=0; ++ int nirq = 0; ++ int slots = 0; ++ int i; ++ ++ /* Set GPIO_A<1:0> to be outputs for PCMCIA power controller: */ ++ PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1); ++ ++ /* Disable Power 3.3V/5V for PCMCIA */ ++ PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1; ++ ++ if (!request_mem_region(_PCCR, 512, "PCMCIA")) ++ return -1; ++ ++ ++ INTPOL1 |= SA1111_IRQMASK_HI(S0_READY_NINT) | ++ SA1111_IRQMASK_HI(S0_CD_VALID) | ++ SA1111_IRQMASK_HI(S0_BVD1_STSCHG); + +- /* Disable Power 3.3V/5V for PCMCIA/CF */ +- PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3; ++ nirq = 2; ++ slots = 1; ++ ++ if (!adsbitsy_smc91111_present()) { ++ /* If the SMC91111 is used CF cannot be used */ ++ /* Set GPIO_A<3:2> to be outputs for CF power controller: */ ++ PA_DDR &= ~(GPIO_GPIO2 | GPIO_GPIO3); ++ ++ /* Disable Power 3.3V/5V for CF */ ++ PA_DWR |= GPIO_GPIO2 | GPIO_GPIO3; ++ ++ INTPOL1 |= SA1111_IRQMASK_HI(S1_READY_NINT) | ++ SA1111_IRQMASK_HI(S1_CD_VALID) | ++ SA1111_IRQMASK_HI(S1_BVD1_STSCHG); ++ ++ nirq = 4; ++ slots = 2; ++ } ++ ++ for (i = ret = 0; i < nirq; i++) { ++ ret = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT, ++ irqs[i].str, NULL); ++ if (ret) ++ break; ++ } + +- /* Why? */ +- MECR = 0x09430943; ++ if (i < nirq) { ++ printk(KERN_ERR "sa1111_pcmcia: unable to grab IRQ%d (%d)\n", ++ irqs[i].irq, ret); ++ while (i--) ++ free_irq(irqs[i].irq, NULL); + +- return sa1111_pcmcia_init(init); ++ release_mem_region(_PCCR, 16); ++ } ++ ++ return ret ? -1 : slots; ++} ++ ++static int adsbitsy_pcmcia_shutdown(void) ++{ ++ ++ free_irq(S0_CD_VALID, NULL); ++ free_irq(S0_BVD1_STSCHG, NULL); ++ INTPOL1 &= ~(SA1111_IRQMASK_HI(S0_CD_VALID) | SA1111_IRQMASK_HI(S0_BVD1_STSCHG)); ++ ++ if (!adsbitsy_smc91111_present()) { ++ free_irq(S1_CD_VALID, NULL); ++ free_irq(S1_BVD1_STSCHG, NULL); ++ INTPOL1 &= ~(SA1111_IRQMASK_HI(S1_CD_VALID) | SA1111_IRQMASK_HI(S1_BVD1_STSCHG)); ++ } ++ ++ return 0; + } + +-static int +-adsbitsy_pcmcia_configure_socket(const struct pcmcia_configure *conf) ++ ++static int adsbitsy_pcmcia_socket_state(struct pcmcia_state_array *state) ++{ ++ unsigned long status; ++ ++ if (adsbitsy_smc91111_present()) { ++ if(state->size<1) return -1; ++ } ++ else ++ if(state->size<2) return -1; ++ ++ memset(state->state, 0, ++ (state->size)*sizeof(struct pcmcia_state)); ++ ++ status = PCSR; ++ ++ state->state[0].detect = status & PCSR_S0_DETECT ? 0 : 1; ++ state->state[0].ready = status & PCSR_S0_READY ? 1 : 0; ++ state->state[0].bvd1 = status & PCSR_S0_BVD1 ? 1 : 0; ++ state->state[0].bvd2 = status & PCSR_S0_BVD2 ? 1 : 0; ++ state->state[0].wrprot = status & PCSR_S0_WP ? 1 : 0; ++ state->state[0].vs_3v = status & PCSR_S0_VS1 ? 0 : 1; ++ state->state[0].vs_Xv = status & PCSR_S0_VS2 ? 0 : 1; ++ ++ if (state->size > 1) { ++ if (adsbitsy_smc91111_present()) { ++ // If there is SMC91111 on ADS Bitsy connector board ++ // it returns not detect/ready/... ++ state->state[1].detect = 0; ++ state->state[1].ready = 0; ++ state->state[1].bvd1 = 0; ++ state->state[1].bvd2 = 0; ++ state->state[1].wrprot = 0; ++ state->state[1].vs_3v = 0; ++ state->state[1].vs_Xv = 0; ++ } ++ else { ++ state->state[1].detect = status & PCSR_S1_DETECT ? 0 : 1; ++ state->state[1].ready = status & PCSR_S1_READY ? 1 : 0; ++ state->state[1].bvd1 = status & PCSR_S1_BVD1 ? 1 : 0; ++ state->state[1].bvd2 = status & PCSR_S1_BVD2 ? 1 : 0; ++ state->state[1].wrprot = status & PCSR_S1_WP ? 1 : 0; ++ state->state[1].vs_3v = status & PCSR_S1_VS1 ? 0 : 1; ++ state->state[1].vs_Xv = status & PCSR_S1_VS2 ? 0 : 1; ++ } ++ } ++ return 1; ++} ++ ++static int adsbitsy_pcmcia_configure_socket(const struct pcmcia_configure *conf) + { + unsigned int pa_dwr_mask, pa_dwr_set; + int ret; +@@ -54,10 +182,11 @@ + + switch (conf->vcc) { + default: +- case 0: pa_dwr_set = 0; break; ++ case 0: pa_dwr_set = GPIO_GPIO2 | GPIO_GPIO3; break; + case 33: pa_dwr_set = GPIO_GPIO2; break; + case 50: pa_dwr_set = GPIO_GPIO3; break; + } ++ break; + + default: + return -1; +@@ -83,8 +212,8 @@ + + struct pcmcia_low_level adsbitsy_pcmcia_ops = { + init: adsbitsy_pcmcia_init, +- shutdown: sa1111_pcmcia_shutdown, +- socket_state: sa1111_pcmcia_socket_state, ++ shutdown: adsbitsy_pcmcia_shutdown, ++ socket_state: adsbitsy_pcmcia_socket_state, + get_irq_info: sa1111_pcmcia_get_irq_info, + configure_socket: adsbitsy_pcmcia_configure_socket, + +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_adsbitsyplus.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_adsbitsyplus.c +--- linux-2.4.26/drivers/pcmcia/sa1100_adsbitsyplus.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_adsbitsyplus.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,236 @@ ++/* ++ * drivers/pcmcia/sa1100_adsbitsyplus.c ++ * ++ * PCMCIA implementation routines for ADS Bitsy Plus ++ * ++ * Created Feb 7, 2003 by Robert Whaley ++ * ++ * This file comes from sa1100_adsbitsy.c of Woojung Huh ++ * ++ */ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "sa1100_generic.h" ++#include "sa1111_generic.h" ++ ++int adsbitsy_smc91111_present(void); ++ ++#ifndef CONFIG_SMC91111 ++#define adsbitsy_smc91111_present() 0 ++#endif ++ ++static struct irqs { ++ int irq; ++ const char *str; ++} irqs[] = { ++ { S0_CD_VALID, "SA1111 PCMCIA card detect" }, ++ { S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1" }, ++ { S1_CD_VALID, "SA1111 CF card detect" }, ++ { S1_BVD1_STSCHG, "SA1111 CF BVD1" }, ++}; ++ ++#define sock0_3_3_reverse_logic() ((ADS_CPLD_IO2 & ADS_IO2_CPLD_REV_MASK) >= ADS_IO2_CPLD_REV_5_MAGIC) ++ ++static int adsbitsyplus_pcmcia_init(struct pcmcia_init *init) ++{ ++ int ret=0; ++ int nirq = 0; ++ int slots = 0; ++ int i; ++ ++ /* Set GPIO_A<1:0> to be outputs for PCMCIA power controller: */ ++ PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1); ++ ++ /* Disable Power 3.3V/5V for PCMCIA */ ++ if (sock0_3_3_reverse_logic()) ++ PA_DWR = (PA_DWR & ~GPIO_GPIO0) | GPIO_GPIO1; ++ else ++ PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1; ++ ++ if (!request_mem_region(_PCCR, 512, "PCMCIA")) ++ return -1; ++ ++ ++ INTPOL1 |= SA1111_IRQMASK_HI(S0_READY_NINT) | ++ SA1111_IRQMASK_HI(S0_CD_VALID) | ++ SA1111_IRQMASK_HI(S0_BVD1_STSCHG); ++ ++ nirq = 2; ++ slots = 1; ++ ++ if (!adsbitsy_smc91111_present()) { ++ /* If the SMC91111 is used CF cannot be used */ ++ /* Set GPIO_A<3:2> to be outputs for CF power controller: */ ++ PA_DDR &= ~(GPIO_GPIO2 | GPIO_GPIO3); ++ ++ /* Disable Power 3.3V/5V for CF */ ++ PA_DWR |= GPIO_GPIO2 | GPIO_GPIO3; ++ ++ INTPOL1 |= SA1111_IRQMASK_HI(S1_READY_NINT) | ++ SA1111_IRQMASK_HI(S1_CD_VALID) | ++ SA1111_IRQMASK_HI(S1_BVD1_STSCHG); ++ ++ nirq = 4; ++ slots = 2; ++ } ++ ++ for (i = ret = 0; i < nirq; i++) { ++ ret = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT, ++ irqs[i].str, NULL); ++ if (ret) ++ break; ++ } ++ ++ if (i < nirq) { ++ printk(KERN_ERR "sa1111_pcmcia: unable to grab IRQ%d (%d)\n", ++ irqs[i].irq, ret); ++ while (i--) ++ free_irq(irqs[i].irq, NULL); ++ ++ release_mem_region(_PCCR, 16); ++ } ++ ++ return ret ? -1 : slots; ++} ++ ++static int adsbitsyplus_pcmcia_shutdown(void) ++{ ++ ++ free_irq(S0_CD_VALID, NULL); ++ free_irq(S0_BVD1_STSCHG, NULL); ++ INTPOL1 &= ~(SA1111_IRQMASK_HI(S0_CD_VALID) | SA1111_IRQMASK_HI(S0_BVD1_STSCHG)); ++ ++ if (!adsbitsy_smc91111_present()) { ++ free_irq(S1_CD_VALID, NULL); ++ free_irq(S1_BVD1_STSCHG, NULL); ++ INTPOL1 &= ~(SA1111_IRQMASK_HI(S1_CD_VALID) | SA1111_IRQMASK_HI(S1_BVD1_STSCHG)); ++ } ++ ++ return 0; ++} ++ ++ ++static int adsbitsyplus_pcmcia_socket_state(struct pcmcia_state_array *state) ++{ ++ unsigned long status; ++ ++ if (adsbitsy_smc91111_present()) { ++ if(state->size<1) return -1; ++ } ++ else ++ if(state->size<2) return -1; ++ ++ memset(state->state, 0, ++ (state->size)*sizeof(struct pcmcia_state)); ++ ++ status = PCSR; ++ ++ state->state[0].detect = status & PCSR_S0_DETECT ? 0 : 1; ++ state->state[0].ready = status & PCSR_S0_READY ? 1 : 0; ++ state->state[0].bvd1 = status & PCSR_S0_BVD1 ? 1 : 0; ++ state->state[0].bvd2 = status & PCSR_S0_BVD2 ? 1 : 0; ++ state->state[0].wrprot = status & PCSR_S0_WP ? 1 : 0; ++ state->state[0].vs_3v = status & PCSR_S0_VS1 ? 0 : 1; ++ state->state[0].vs_Xv = status & PCSR_S0_VS2 ? 0 : 1; ++ ++ if (state->size > 1) { ++ if (adsbitsy_smc91111_present()) { ++ // If there is SMC91111 on ADS Bitsy connector board ++ // it returns not detect/ready/... ++ state->state[1].detect = 0; ++ state->state[1].ready = 0; ++ state->state[1].bvd1 = 0; ++ state->state[1].bvd2 = 0; ++ state->state[1].wrprot = 0; ++ state->state[1].vs_3v = 0; ++ state->state[1].vs_Xv = 0; ++ } ++ else { ++ state->state[1].detect = status & PCSR_S1_DETECT ? 0 : 1; ++ state->state[1].ready = status & PCSR_S1_READY ? 1 : 0; ++ state->state[1].bvd1 = status & PCSR_S1_BVD1 ? 1 : 0; ++ state->state[1].bvd2 = status & PCSR_S1_BVD2 ? 1 : 0; ++ state->state[1].wrprot = status & PCSR_S1_WP ? 1 : 0; ++ state->state[1].vs_3v = status & PCSR_S1_VS1 ? 0 : 1; ++ state->state[1].vs_Xv = status & PCSR_S1_VS2 ? 0 : 1; ++ } ++ } ++ return 1; ++} ++ ++static int adsbitsyplus_pcmcia_configure_socket(const struct pcmcia_configure *conf) ++{ ++ unsigned int pa_dwr_mask, pa_dwr_set; ++ int ret; ++ ++ switch (conf->sock) { ++ case 0: ++ pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1; ++ ++ if (sock0_3_3_reverse_logic()) { ++ switch (conf->vcc) { ++ default: ++ case 0: pa_dwr_set = GPIO_GPIO1; break; ++ case 33: pa_dwr_set = GPIO_GPIO0 | GPIO_GPIO1; break; ++ case 50: pa_dwr_set = 0; break; ++ } ++ } else { ++ switch (conf->vcc) { ++ default: ++ case 0: pa_dwr_set = GPIO_GPIO0 | GPIO_GPIO1; break; ++ case 33: pa_dwr_set = GPIO_GPIO1; break; ++ case 50: pa_dwr_set = GPIO_GPIO0; break; ++ } ++ } ++ break; ++ ++ case 1: ++ pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3; ++ ++ switch (conf->vcc) { ++ default: ++ case 0: pa_dwr_set = GPIO_GPIO2 | GPIO_GPIO3; break; ++ case 33: pa_dwr_set = GPIO_GPIO2; break; ++ case 50: pa_dwr_set = GPIO_GPIO3; break; ++ } ++ break; ++ ++ default: ++ return -1; ++ } ++ ++ if (conf->vpp != conf->vcc && conf->vpp != 0) { ++ printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n", ++ __FUNCTION__, conf->vpp); ++ return -1; ++ } ++ ++ ret = sa1111_pcmcia_configure_socket(conf); ++ if (ret == 0) { ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set; ++ local_irq_restore(flags); ++ } ++ ++ return ret; ++} ++ ++struct pcmcia_low_level adsbitsyplus_pcmcia_ops = { ++ init: adsbitsyplus_pcmcia_init, ++ shutdown: adsbitsyplus_pcmcia_shutdown, ++ socket_state: adsbitsyplus_pcmcia_socket_state, ++ get_irq_info: sa1111_pcmcia_get_irq_info, ++ configure_socket: adsbitsyplus_pcmcia_configure_socket, ++ ++ socket_init: sa1111_pcmcia_socket_init, ++ socket_suspend: sa1111_pcmcia_socket_suspend, ++}; ++ +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_freebird.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_freebird.c +--- linux-2.4.26/drivers/pcmcia/sa1100_freebird.c 2002-08-03 01:39:44.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_freebird.c 2004-01-14 21:32:26.000000000 +0000 +@@ -67,9 +67,6 @@ + + if(state_array->size<2) return -1; + +- memset(state_array->state, 0, +- (state_array->size)*sizeof(struct pcmcia_state)); +- + levels = LINKUP_PRS; + //printk("LINKUP_PRS=%x \n",levels); + +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_generic.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_generic.c +--- linux-2.4.26/drivers/pcmcia/sa1100_generic.c 2003-06-13 15:51:35.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_generic.c 2004-01-14 21:32:26.000000000 +0000 +@@ -992,10 +992,18 @@ + if(machine_is_graphicsmaster()) + pcmcia_low_level = &graphicsmaster_pcmcia_ops; + #endif ++#ifdef CONFIG_SA1100_ADSAGC ++ if(machine_is_adsagc()) ++ pcmcia_low_level = &graphicsmaster_pcmcia_ops; ++#endif + #ifdef CONFIG_SA1100_ADSBITSY + if(machine_is_adsbitsy()) + pcmcia_low_level = &adsbitsy_pcmcia_ops; + #endif ++#ifdef CONFIG_SA1100_ADSBITSYPLUS ++ if(machine_is_adsbitsyplus()) ++ pcmcia_low_level = &adsbitsyplus_pcmcia_ops; ++#endif + #ifdef CONFIG_SA1100_STORK + if(machine_is_stork()) + pcmcia_low_level = &stork_pcmcia_ops; +@@ -1067,7 +1075,7 @@ + * We initialize the MECR to default values here, because we are + * not guaranteed to see a SetIOMap operation at runtime. + */ +- mecr = 0; ++ mecr = MECR; + + clock = cpufreq_get(0); + +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_graphicsclient.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_graphicsclient.c +--- linux-2.4.26/drivers/pcmcia/sa1100_graphicsclient.c 2002-08-03 01:39:44.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_graphicsclient.c 2004-01-14 21:32:26.000000000 +0000 +@@ -3,6 +3,8 @@ + * + * PCMCIA implementation routines for Graphics Client Plus + * ++ * Nov/14/01 Woojung ++ * Set MECR at initializing time + * 9/12/01 Woojung + * Turn power OFF at startup + * 1/31/2001 Woojung Huh +@@ -19,11 +21,6 @@ + #include + #include "sa1100_generic.h" + +-#error This is broken! +- +-#define S0_CD_IRQ 60 // Socket 0 Card Detect IRQ +-#define S0_STS_IRQ 55 // Socket 0 PCMCIA IRQ +- + static volatile unsigned long *PCMCIA_Status = + ((volatile unsigned long *) ADS_p2v(_ADS_CS_STATUS)); + +@@ -46,20 +43,23 @@ + *PCMCIA_Power &= ~0x03; + + /* Register interrupts */ +- irq = S0_CD_IRQ; ++ irq = IRQ_GRAPHICSCLIENT_S0_CD; + res = request_irq(irq, init->handler, SA_INTERRUPT, "PCMCIA 0 CD", NULL); + if (res < 0) { +- printk(KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq); ++ printk(KERN_ERR "%s: Request for IRQ %u failed\n", __FUNCTION__, irq); + return -1; + } + ++ // Nov/14/01 WH ++ MECR = 0x00000943; ++ + return 1; // 1 PCMCIA Slot + } + + static int gcplus_pcmcia_shutdown(void) + { + /* disable IRQs */ +- free_irq( S0_CD_IRQ, NULL); ++ free_irq( IRQ_GRAPHICSCLIENT_S0_CD, NULL); + + /* Shutdown PCMCIA power */ + mdelay(2); // 2msec +@@ -74,9 +74,6 @@ + + if(state_array->size<1) return -1; + +- memset(state_array->state, 0, +- (state_array->size)*sizeof(struct pcmcia_state)); +- + levels=*PCMCIA_Status; + + state_array->state[0].detect=(levels & ADS_CS_ST_A_CD)?1:0; +@@ -96,7 +93,7 @@ + return -1; + + if (info->sock == 0) +- info->irq = S0_STS_IRQ; ++ info->irq = IRQ_GRAPHICSCLIENT_S0_STS; + + return 0; + } +@@ -143,6 +140,11 @@ + + restore_flags(flags); + ++ if (configure->irq) ++ enable_irq(IRQ_GRAPHICSCLIENT_S0_STS); ++ else ++ disable_irq(IRQ_GRAPHICSCLIENT_S0_STS); ++ + return 0; + } + +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_graphicsmaster.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_graphicsmaster.c +--- linux-2.4.26/drivers/pcmcia/sa1100_graphicsmaster.c 2002-08-03 01:39:44.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_graphicsmaster.c 2004-01-14 21:32:26.000000000 +0000 +@@ -12,13 +12,13 @@ + #include + + #include ++#include + + #include "sa1100_generic.h" + #include "sa1111_generic.h" + + static int graphicsmaster_pcmcia_init(struct pcmcia_init *init) + { +- int return_val=0; + + /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */ + PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); +@@ -26,9 +26,6 @@ + /* Disable Power 3.3V/5V for PCMCIA/CF */ + PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3; + +- /* why? */ +- MECR = 0x09430943; +- + return sa1111_pcmcia_init(init); + } + +@@ -59,6 +56,10 @@ + case 33: pa_dwr_set = GPIO_GPIO3; break; + case 50: pa_dwr_set = GPIO_GPIO2; break; + } ++ break; ++ ++ default: ++ return -1; + } + + if (conf->vpp != conf->vcc && conf->vpp != 0) { +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_h3600.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_h3600.c +--- linux-2.4.26/drivers/pcmcia/sa1100_h3600.c 2002-08-03 01:39:44.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_h3600.c 2004-01-14 21:32:26.000000000 +0000 +@@ -29,6 +29,9 @@ + set_GPIO_IRQ_edge(GPIO_H3600_PCMCIA_IRQ0 | GPIO_H3600_PCMCIA_IRQ1, + GPIO_FALLING_EDGE); + ++ set_GPIO_IRQ_edge(GPIO_H3600_PCMCIA_CD0 | GPIO_H3600_PCMCIA_CD1, ++ GPIO_NO_EDGES); ++ + /* + * Register interrupts + */ +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_jornada720.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_jornada720.c +--- linux-2.4.26/drivers/pcmcia/sa1100_jornada720.c 2002-08-03 01:39:44.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_jornada720.c 2004-01-14 21:32:26.000000000 +0000 +@@ -8,6 +8,7 @@ + #include + + #include ++#include + + #include "sa1100_generic.h" + #include "sa1111_generic.h" +@@ -88,7 +89,7 @@ + + local_irq_save(flags); + PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set; +- locla_irq_restore(flags); ++ local_irq_restore(flags); + } + + return ret; +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_pangolin.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_pangolin.c +--- linux-2.4.26/drivers/pcmcia/sa1100_pangolin.c 2002-08-03 01:39:44.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_pangolin.c 2004-01-14 21:32:26.000000000 +0000 +@@ -53,9 +53,6 @@ + + if(state_array->size<2) return -1; + +- memset(state_array->state, 0, +- (state_array->size)*sizeof(struct pcmcia_state)); +- + levels=GPLR; + #ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE + state_array->state[1].detect=((levels & GPIO_PCMCIA_CD)==0)?1:0; +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_shannon.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_shannon.c +--- linux-2.4.26/drivers/pcmcia/sa1100_shannon.c 2002-08-03 01:39:44.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_shannon.c 2004-01-14 21:32:26.000000000 +0000 +@@ -53,9 +53,6 @@ + { + unsigned long levels; + +- memset(state_array->state, 0, +- state_array->size * sizeof(struct pcmcia_state)); +- + levels = GPLR; + + state_array->state[0].detect = (levels & SHANNON_GPIO_EJECT_0) ? 0 : 1; +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_simpad.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_simpad.c +--- linux-2.4.26/drivers/pcmcia/sa1100_simpad.c 2002-08-03 01:39:44.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_simpad.c 2004-01-14 21:32:26.000000000 +0000 +@@ -63,9 +63,6 @@ + + if(state_array->size<2) return -1; + +- memset(state_array->state, 0, +- (state_array->size)*sizeof(struct pcmcia_state)); +- + levels=GPLR; + + state_array->state[1].detect=((levels & GPIO_CF_CD)==0)?1:0; +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_stork.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_stork.c +--- linux-2.4.26/drivers/pcmcia/sa1100_stork.c 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_stork.c 2004-02-23 13:36:32.000000000 +0000 +@@ -79,9 +79,6 @@ + + if(state_array->size<2) return -1; + +- memset(state_array->state, 0, +- (state_array->size)*sizeof(struct pcmcia_state)); +- + levels=GPLR; + + if (debug > 1) +diff -urN linux-2.4.26/drivers/pcmcia/sa1100_yopy.c linux-2.4.26-vrs1/drivers/pcmcia/sa1100_yopy.c +--- linux-2.4.26/drivers/pcmcia/sa1100_yopy.c 2002-08-03 01:39:44.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pcmcia/sa1100_yopy.c 2004-01-14 21:32:26.000000000 +0000 +@@ -73,9 +73,6 @@ + if (state_array->size != 1) + return -1; + +- memset(state_array->state, 0, +- state_array->size * sizeof(struct pcmcia_state)); +- + levels = GPLR; + + state_array->state[0].detect = (levels & GPIO_CF_CD) ? 0 : 1; +diff -urN linux-2.4.26/drivers/pld/Makefile linux-2.4.26-vrs1/drivers/pld/Makefile +--- linux-2.4.26/drivers/pld/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pld/Makefile 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,28 @@ ++# ++# Makefile for the kernel pld device drivers. ++# ++# Note! Dependencies are done automagically by 'make dep', which also ++# removes any old dependencies. DON'T put your own dependencies here ++# unless it's something special (ie not a .c file). ++# ++# Note 2! The CFLAGS definitions are now inherited from the ++# parent makes.. ++# ++# $Id: $ ++# ++ ++O_TARGET := pld.o ++ ++export-objs := pld_hotswap.o ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++obj-$(CONFIG_PLD) += pld_epxa.o ++obj-$(CONFIG_PLD_HOTSWAP) += pld_hotswap.o ++ ++include $(TOPDIR)/Rules.make ++ ++fastdep: ++ +diff -urN linux-2.4.26/drivers/pld/pld_epxa.c linux-2.4.26-vrs1/drivers/pld/pld_epxa.c +--- linux-2.4.26/drivers/pld/pld_epxa.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pld/pld_epxa.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,375 @@ ++ ++/* ++ * drivers/char/epxapld.c ++ * ++ * Copyright (C) 2001 Altera Corporation ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define PLD_CONF00_TYPE (volatile unsigned int *) ++#define MODE_CTRL00_TYPE (volatile unsigned int *) ++//#define DEBUG(x) x ++#define DEBUG(x) ++ ++#include ++#include ++#ifdef CONFIG_PLD_HOTSWAP ++#include ++#endif ++#include ++ ++/* ++ * Macros ++ */ ++ ++ ++#define PLD_BASE (IO_ADDRESS(EXC_PLD_CONFIG00_BASE)) ++#define CLOCK_DIV_RATIO ((1 + EXC_AHB2_CLK_FREQUENCY/32000000) & CONFIG_CONTROL_CLOCK_RATIO_MSK) ++/* ++ * STRUCTURES ++ */ ++ ++ ++struct pld_sbihdr{ ++ unsigned int fpos; ++ unsigned int temp; ++}; ++ ++static DECLARE_MUTEX(pld_sem); ++ ++ ++static void lock_pld (void) ++{ ++ /* Lock the pld i/f */ ++ unsigned int tmp; ++ ++ tmp = readl(CONFIG_CONTROL(PLD_BASE)); ++ tmp |= CONFIG_CONTROL_LK_MSK; ++ ++ writel(tmp,CONFIG_CONTROL(PLD_BASE)); ++} ++ ++static void unlock_excalibur_pld (void) ++{ ++ /* Unlock the pld i/f */ ++ ++ if (readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_LK_MSK ){ ++ writel(CONFIG_UNLOCK_MAGIC, CONFIG_UNLOCK(PLD_BASE)); ++ while (readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_LK_MSK); ++ } ++} ++ ++ ++static int place_pld_into_configure_mode (void) ++{ ++ unsigned int tmp; ++ ++ ++ if( readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_CO_MSK ){ ++ /* ++ * Already being configured!!! ++ */ ++ printk(KERN_WARNING "pld0: Device already being configured!\n"); ++ return -EBUSY; ++ } ++ ++ /* Set up the config clock */ ++ ++ writel(CLOCK_DIV_RATIO,CONFIG_CONTROL_CLOCK(PLD_BASE)); ++ while(readl(CONFIG_CONTROL_CLOCK(PLD_BASE)) ++ !=CLOCK_DIV_RATIO); ++ /* Tell the device we wish to configure it */ ++ tmp = readl(CONFIG_CONTROL(PLD_BASE)); ++ tmp |= CONFIG_CONTROL_CO_MSK; ++ writel(tmp,CONFIG_CONTROL(PLD_BASE)); ++ ++ ++ /* ++ * Wait for the busy bit to clear then check for errors. ++ */ ++ ++ while((tmp=readl(CONFIG_CONTROL(PLD_BASE))&CONFIG_CONTROL_B_MSK )); ++ ++ if ( tmp & CONFIG_CONTROL_E_MSK ){ ++ if ( tmp & CONFIG_CONTROL_ES_0_MSK ){ ++ /* Already being programmed via JTAG */ ++ printk(KERN_WARNING "pld0:JTAG configuration alreay in progress\n"); ++ return -EBUSY; ++ ++ } ++ if ( tmp & CONFIG_CONTROL_ES_1_MSK ){ ++ /* No config clock configured */ ++ printk(KERN_ERR "pld0:No config clock!\n"); ++ BUG(); ++ return -EBUSY; ++ } ++ if ( tmp & CONFIG_CONTROL_ES_2_MSK ){ ++ /* Already being programmed via External device */ ++ printk(KERN_WARNING "pld0:JTAG configuration alreay in progress\n"); ++ return -EBUSY; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++static int write_pld_data_word(unsigned int config_data) ++{ ++ unsigned int tmp; ++ ++ do{ ++ tmp = readl(CONFIG_CONTROL(PLD_BASE)); ++ } ++ while ( ( tmp & CONFIG_CONTROL_B_MSK ) && ++ !( tmp & CONFIG_CONTROL_E_MSK )); ++ ++ if ( tmp & CONFIG_CONTROL_E_MSK ){ ++ printk("pld0: Error writing pld data, CONFIG_CONTROL=%#x\n",tmp); ++ ++ return -EILSEQ; ++ } ++ ++ writel(config_data,CONFIG_CONTROL_DATA(PLD_BASE)); ++ return 0; ++ ++} ++ ++ ++static int wait_for_device_to_configure (void) ++{ ++ int i=0x10000; ++ ++ while(readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_B_MSK); ++ ++ /* ++ * Wait for the config bit (CO) to go low, indicating that everything ++ * is Ok. If it doesn't, assume that is screwed up somehow and ++ * clear the CO bit to abort the configuration. ++ */ ++ ++ while(readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_B_MSK); ++ ++ while (i&&(readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_CO_MSK)){ ++ i--; ++ } ++ ++ if (!i){ ++ writel(0,CONFIG_CONTROL(PLD_BASE)); ++ printk(KERN_WARNING "pld0: Invalid PLD config data\n"); ++ return -EILSEQ; ++ } ++ ++ return 0; ++} ++ ++ ++ ++static int pld_open(struct inode* inode, struct file *filep) ++{ ++ ++ struct pld_sbihdr* sbihdr; ++ ++ /* Check the device minor number */ ++ if(minor(inode->i_rdev)){ ++ DEBUG(printk("pld0: minor=%d",minor(inode->i_rdev));) ++ return -ENODEV; ++ } ++ ++ /* Create our private data and link it to the file structure */ ++ sbihdr=kmalloc(sizeof(struct pld_sbihdr),GFP_KERNEL); ++ ++ if(!sbihdr) ++ return -ENOMEM; ++ ++ filep->private_data=sbihdr; ++ ++ sbihdr->fpos=0; ++ sbihdr->temp=0; ++ return 0; ++} ++ ++static int pld_release(struct inode* inode, struct file *filep){ ++ int ret_code; ++ ++ kfree(filep->private_data); ++ ret_code=wait_for_device_to_configure(); ++ lock_pld(); ++ return ret_code; ++} ++ ++static ssize_t pld_write(struct file* filep, const char* data, size_t count, loff_t* ppos){ ++ ++ struct pld_sbihdr* sbihdr=filep->private_data; ++ int bytes_left=count; ++ int result; ++ DEBUG(int i=0); ++ ++ ++ /* Can't seek (pwrite) on pld. */ ++ if (ppos != &filep->f_pos) ++ return -ESPIPE; ++ ++ ++ /* Check access to the whole are in one go */ ++ if(!access_ok(VERIFY_READ,(const void*)data, count)){ ++ return -EFAULT; ++ } ++ ++ /* ++ * We now lock against writes. ++ */ ++ if (down_interruptible(&pld_sem)) ++ return -ERESTARTSYS; ++ ++ if(!sbihdr->fpos){ ++ /* ++ * unlock the pld and place in configure mode ++ */ ++ unlock_excalibur_pld(); ++ result=place_pld_into_configure_mode(); ++ if(result) ++ return result; ++ } ++ DEBUG(printk("data= %#x count=%#x 0ffset=%#x\n",*data, count, *ppos)); ++ ++ while(bytes_left){ ++ char tmp; ++ __get_user(tmp,data); ++ /* read our header ! */ ++ sbihdr->temp|=tmp << (8*(sbihdr->fpos&3)); ++ if((sbihdr->fpos&3)==3){ ++ if(write_pld_data_word(sbihdr->temp)) ++ { ++ DEBUG(printk("pos=%d\n",sbihdr->fpos);) ++ return -EILSEQ; ++ } ++ DEBUG(if(i<10){) ++ DEBUG(printk("fpos2 :%#x data=%#x\n",sbihdr->fpos,sbihdr->temp)); ++ DEBUG(i++); ++ DEBUG(}); ++ sbihdr->temp=0; ++ DEBUG(words_written++); ++ } ++ sbihdr->fpos++; ++ data++; ++ bytes_left--; ++ } ++ ++ up(&pld_sem); ++ return (count); ++} ++ ++int pld_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg) ++{ ++ ++ switch(cmd){ ++ ++#ifdef CONFIG_PLD_HOTSWAP ++ case PLD_IOC_ADD_PLD_DEV: ++ { ++ struct pldhs_dev_desc desc; ++ struct pldhs_dev_info info; ++ char *name; ++ void *data; ++ int result=0; ++ ++ result=copy_from_user(&desc,(const void*)arg,sizeof(struct pldhs_dev_desc)); ++ if(result) ++ return -EFAULT; ++ result=copy_from_user(&info,(const void*)desc.info,sizeof(struct pldhs_dev_info)); ++ if(result) ++ return -EFAULT; ++ name=kmalloc(info.nsize,GFP_KERNEL); ++ if(!name) ++ return -ENOMEM; ++ ++ result=copy_from_user(name,(const void*)desc.name,info.nsize); ++ if(result){ ++ result=-EFAULT; ++ goto ioctl_out; ++ } ++ ++ data=kmalloc(info.pssize,GFP_KERNEL); ++ if(!data){ ++ result=-ENOMEM; ++ goto ioctl_out; ++ } ++ ++ result=copy_from_user(data,(const void*)desc.data,info.pssize); ++ if(result){ ++ result=-EFAULT; ++ goto ioctl_out1; ++ } ++ result=pldhs_add_device(&info,name,data); ++ ++ ioctl_out1: ++ kfree(data); ++ ioctl_out: ++ kfree(name); ++ return result; ++ ++ } ++ ++ case PLD_IOC_REMOVE_PLD_DEVS: ++ pldhs_remove_devices(); ++ return 0; ++ ++ case PLD_IOC_SET_INT_MODE: ++ if(cmd==3){ ++ printk(KERN_INFO "Interrupt mode set to 3 (Six individual interrupts)\n"); ++ return 0; ++ }else{ ++ printk(KERN_INFO "There is no interrupt handler available for this mode (%d). You will need to add one\n to implement whatever scheme you require\n"); ++ return -EACCES; ++ } ++#endif ++ default: ++ return -ENOTTY; ++ } ++} ++ ++ ++static struct file_operations pld_fops={ ++ write: pld_write, ++ ioctl: pld_ioctl, ++ open: pld_open, ++ release: pld_release ++}; ++ ++static int __init pld_init(void){ ++ int major; ++ major=register_chrdev(0,"pld",&pld_fops); ++ printk(KERN_INFO "Using PLD major num=%d\n",major); ++ if (major<0){ ++ return major; ++ } ++ return 0; ++} ++ ++__initcall(pld_init); +diff -urN linux-2.4.26/drivers/pld/pld_hotswap.c linux-2.4.26-vrs1/drivers/pld/pld_hotswap.c +--- linux-2.4.26/drivers/pld/pld_hotswap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/pld/pld_hotswap.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,188 @@ ++/* ++ * linux/drivers/pld/pld_hotswap.c ++ * ++ * Pld driver for Altera EPXA Excalibur devices ++ * ++ * ++ * Copyright 2001 Altera Corporation (cdavies@altera.com) ++ * ++ * 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 ++ * ++ * $Id: $ ++ * ++ */ ++ ++/* ++ * pld_hotswap ops contains the basic operation required for adding ++ * and removing devices from the system. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++static struct pld_hotswap_ops pldhs_driver_list={ ++ list: LIST_HEAD_INIT(pldhs_driver_list.list), ++ name: "", ++}; ++ ++static spinlock_t list_lock=SPIN_LOCK_UNLOCKED; ++ ++ ++ ++static struct pld_hotswap_ops * pldhs_find_driver(char* name) ++{ ++ struct pld_hotswap_ops * ptr; ++ struct list_head *list_pos; ++ ++ spin_lock(&list_lock); ++ list_for_each(list_pos,&pldhs_driver_list.list){ ++ ptr=(struct pld_hotswap_ops *)list_pos; ++ if(!strcmp(name, ptr->name)){ ++ spin_unlock(&list_lock); ++ return ptr; ++ ++ } ++ } ++ spin_unlock(&list_lock); ++ return 0; ++} ++ ++ ++ ++int pldhs_register_driver(struct pld_hotswap_ops *drv) ++{ ++ ++ /* Check that the device is not already on the list ++ * if so, do nothing */ ++ if(pldhs_find_driver(drv->name)){ ++ return 0; ++ } ++ ++ printk(KERN_INFO "PLD: Registering hotswap driver %s\n",drv->name); ++ /* Add this at the start of the list */ ++ spin_lock(&list_lock); ++ list_add((struct list_head*)drv,&pldhs_driver_list.list); ++ spin_unlock(&list_lock); ++ ++ return 0; ++} ++ ++int pldhs_unregister_driver(char *name) ++{ ++ struct pld_hotswap_ops *ptr; ++ ++ ptr=pldhs_find_driver(name); ++ if(!ptr){ ++ return -ENODEV; ++ } ++ ++ printk(KERN_INFO "PLD: Unregistering hotswap driver%s\n",name); ++ spin_lock(&list_lock); ++ list_del((struct list_head*)ptr); ++ spin_unlock(&list_lock); ++ ++ return 0; ++} ++ ++int pldhs_add_device(struct pldhs_dev_info* dev_info,char *drv_name, void* dev_ps_data) ++{ ++ struct pld_hotswap_ops * ptr; ++ ++ ptr=pldhs_find_driver(drv_name); ++ ++ if(!ptr){ ++ /* try requesting this module*/ ++ request_module(drv_name); ++ /* is the driver there now? */ ++ ptr=pldhs_find_driver(drv_name); ++ if(!ptr){ ++ printk("pld hotswap: Failed to load a driver for %s\n",drv_name); ++ return -ENODEV; ++ } ++ } ++ ++ if(!ptr->add_device){ ++ printk(KERN_WARNING "pldhs: no add_device() function for driver %s\n",drv_name); ++ return 0; ++ } ++ ++ return ptr->add_device(dev_info,dev_ps_data); ++} ++ ++int pldhs_remove_devices(void) ++{ ++ struct list_head *list_pos; ++ struct pld_hotswap_ops * ptr; ++ ++ ++ spin_lock(&list_lock); ++ list_for_each(list_pos,&pldhs_driver_list.list){ ++ ptr=(struct pld_hotswap_ops *)list_pos; ++ if(ptr->remove_devices) ++ ptr->remove_devices(); ++ ++ } ++ spin_unlock(&list_lock); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PROC_FS ++int pldhs_read_proc(char* buf,char** start,off_t offset,int count,int *eof,void *data){ ++ ++ ++ struct list_head *list_pos; ++ struct pld_hotswap_ops * ptr; ++ int i,len=0; ++ ++ *start=buf; ++ spin_lock(&list_lock); ++ list_for_each(list_pos,&pldhs_driver_list.list){ ++ ptr=(struct pld_hotswap_ops *)list_pos; ++ if(ptr->proc_read){ ++ i=ptr->proc_read(buf,start,offset,count,eof,data); ++ count-=i; ++ len+=i; ++ *start+=i; ++ } ++ } ++ spin_unlock(&list_lock); ++ ++ *start=NULL; ++ *eof=1; ++ return len; ++} ++ ++void __init pldhs_init(void){ ++ create_proc_read_entry("pld",0,NULL,pldhs_read_proc,NULL); ++} ++ ++__initcall(pldhs_init); ++#endif ++ ++EXPORT_SYMBOL(pldhs_register_driver); ++EXPORT_SYMBOL(pldhs_unregister_driver); +diff -urN linux-2.4.26/drivers/scsi/Makefile linux-2.4.26-vrs1/drivers/scsi/Makefile +--- linux-2.4.26/drivers/scsi/Makefile 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/scsi/Makefile 2004-02-23 13:36:33.000000000 +0000 +@@ -21,7 +21,7 @@ + + O_TARGET := scsidrv.o + +-export-objs := scsi_syms.o 53c700.o ++export-objs := scsi_syms.o 53c700.o scsi_error.o + mod-subdirs := pcmcia ../acorn/scsi + + +diff -urN linux-2.4.26/drivers/scsi/scsi.c linux-2.4.26-vrs1/drivers/scsi/scsi.c +--- linux-2.4.26/drivers/scsi/scsi.c 2003-08-25 12:44:42.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/scsi/scsi.c 2004-01-14 21:39:12.000000000 +0000 +@@ -1334,14 +1334,10 @@ + */ + int scsi_retry_command(Scsi_Cmnd * SCpnt) + { +- memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, +- sizeof(SCpnt->data_cmnd)); +- SCpnt->request_buffer = SCpnt->buffer; +- SCpnt->request_bufflen = SCpnt->bufflen; +- SCpnt->use_sg = SCpnt->old_use_sg; +- SCpnt->cmd_len = SCpnt->old_cmd_len; +- SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; +- SCpnt->underflow = SCpnt->old_underflow; ++ /* ++ * Restore the SCSI command state. ++ */ ++ scsi_setup_cmd_retry(SCpnt); + + /* + * Zero the sense information from the last time we tried +diff -urN linux-2.4.26/drivers/scsi/scsi.h linux-2.4.26-vrs1/drivers/scsi/scsi.h +--- linux-2.4.26/drivers/scsi/scsi.h 2003-08-25 12:44:42.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/scsi/scsi.h 2004-04-19 18:46:03.000000000 +0100 +@@ -465,6 +465,7 @@ + int sectors); + extern struct Scsi_Device_Template *scsi_get_request_dev(struct request *); + extern int scsi_init_cmd_errh(Scsi_Cmnd * SCpnt); ++extern void scsi_setup_cmd_retry(Scsi_Cmnd *SCpnt); + extern int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int); + extern void scsi_io_completion(Scsi_Cmnd * SCpnt, int good_sectors, + int block_sectors); +@@ -588,6 +589,7 @@ + unsigned changed:1; /* Data invalid due to media change */ + unsigned busy:1; /* Used to prevent races */ + unsigned lockable:1; /* Able to prevent media removal */ ++ unsigned locked:1; /* Media removal disabled */ + unsigned borken:1; /* Tell the Seagate driver to be + * painfully slow on this device */ + unsigned tagged_supported:1; /* Supports SCSI-II tagged queuing */ +diff -urN linux-2.4.26/drivers/scsi/scsi_dma.c linux-2.4.26-vrs1/drivers/scsi/scsi_dma.c +--- linux-2.4.26/drivers/scsi/scsi_dma.c 2002-02-25 19:38:04.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/scsi/scsi_dma.c 2004-01-14 21:32:26.000000000 +0000 +@@ -30,6 +30,15 @@ + typedef unsigned char FreeSectorBitmap; + #elif SECTORS_PER_PAGE <= 32 + typedef unsigned int FreeSectorBitmap; ++#elif SECTORS_PER_PAGE <= 64 ++#if 0 ++typedef unsigned long long FreeSectorBitmap; ++#else ++typedef struct { ++ unsigned long l,h; ++} FreeSectorBitmap; ++#define LARGE_MALLOC ++#endif + #else + #error You lose. + #endif +@@ -69,6 +78,7 @@ + * to allocate more memory in order to be able to write the + * data to disk, you would wedge the system. + */ ++#ifndef LARGE_MALLOC + void *scsi_malloc(unsigned int len) + { + unsigned int nbits, mask; +@@ -167,6 +177,97 @@ + panic("scsi_free:Bad offset"); + } + ++#else ++ ++void *scsi_malloc(unsigned int len) ++{ ++ unsigned int nbits; ++ unsigned long maskl, maskh, flags; ++ FreeSectorBitmap *fsb; ++ int i; ++ ++ if (len % SECTOR_SIZE != 0 || len > PAGE_SIZE) ++ return NULL; ++ ++ save_flags_cli (flags); ++ nbits = len >> 9; ++ if (nbits < 32) { ++ maskl = (1 << nbits) - 1; ++ maskh = 0; ++ } else { ++ maskl = (unsigned long)-1; ++ maskh = (1 << (nbits - 32)) - 1; ++ } ++ ++ fsb = dma_malloc_freelist; ++ ++ for (i = 0; i < dma_sectors / SECTORS_PER_PAGE; i++) { ++ unsigned long mml, mmh; ++ int j; ++ mml = maskl; ++ mmh = maskh; ++ j = 0; ++ do { ++ if ((fsb->l & mml) == 0 && (fsb->h & mmh) == 0) { ++ fsb->h |= mmh; ++ fsb->l |= mml; ++ restore_flags (flags); ++ scsi_dma_free_sectors -= nbits; ++#ifdef DEBUG ++ printk("SMalloc: %d %p\n",len, dma_malloc_pages[i] + (j << 9)); ++#endif ++ return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9)); ++ } ++ mmh = (mmh << 1) | (mml >> 31); ++ mml <<= 1; ++ j++; ++ } while (!(mmh & (1 << 31))); ++ fsb ++; ++ } ++ return NULL; /* Nope. No more */ ++} ++ ++int scsi_free(void *obj, unsigned int len) ++{ ++ unsigned int page, sector, nbits; ++ unsigned long maskl, maskh, flags; ++ ++#ifdef DEBUG ++ printk("scsi_free %p %d\n",obj, len); ++#endif ++ ++ for (page = 0; page < dma_sectors / SECTORS_PER_PAGE; page++) { ++ unsigned long page_addr = (unsigned long) dma_malloc_pages[page]; ++ if ((unsigned long) obj >= page_addr && ++ (unsigned long) obj < page_addr + PAGE_SIZE) { ++ sector = (((unsigned long) obj) - page_addr) >> 9; ++ nbits = len >> 9; ++ if (nbits < 32) { ++ maskl = (1 << nbits) - 1; ++ maskh = 0; ++ } else { ++ maskl = (unsigned long)-1; ++ maskh = (1 << (nbits - 32)) - 1; ++ } ++ if ((sector + nbits) > SECTORS_PER_PAGE) ++ panic ("scsi_free:Bad memory alignment"); ++ maskh = (maskh << sector) | (maskl >> (32 - sector)); ++ maskl = maskl << sector; ++ save_flags_cli(flags); ++ if (((dma_malloc_freelist[page].l & maskl) != maskl) || ++ ((dma_malloc_freelist[page].h & maskh) != maskh)) ++ panic("scsi_free:Trying to free unused memory"); ++ scsi_dma_free_sectors += nbits; ++ dma_malloc_freelist[page].l &= ~maskl; ++ dma_malloc_freelist[page].h &= ~maskh; ++ restore_flags(flags); ++ return 0; ++ } ++ } ++ panic("scsi_free:Bad offset"); ++} ++#endif ++ + + /* + * Function: scsi_resize_dma_pool +diff -urN linux-2.4.26/drivers/scsi/scsi_error.c linux-2.4.26-vrs1/drivers/scsi/scsi_error.c +--- linux-2.4.26/drivers/scsi/scsi_error.c 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/scsi/scsi_error.c 2004-04-18 21:47:51.000000000 +0100 +@@ -35,6 +35,8 @@ + #include "hosts.h" + #include "constants.h" + ++#include /* grr */ ++ + /* + * We must always allow SHUTDOWN_SIGS. Even if we are not a module, + * the host drivers that we are using may be loaded as modules, and +@@ -49,6 +51,13 @@ + */ + #define SHUTDOWN_SIGS (sigmask(SIGHUP)) + ++/* ++ * The number of times we retry a REQUEST SENSE and TEST UNIT READY ++ * respectively. This is arbitary. ++ */ ++#define SENSE_RETRIES 5 ++#define TUR_RETRIES 5 ++ + #ifdef DEBUG + #define SENSE_TIMEOUT SCSI_TIMEOUT + #define ABORT_TIMEOUT SCSI_TIMEOUT +@@ -373,16 +382,12 @@ + */ + STATIC int scsi_eh_retry_command(Scsi_Cmnd * SCpnt) + { +- memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, +- sizeof(SCpnt->data_cmnd)); +- SCpnt->request_buffer = SCpnt->buffer; +- SCpnt->request_bufflen = SCpnt->bufflen; +- SCpnt->use_sg = SCpnt->old_use_sg; +- SCpnt->cmd_len = SCpnt->old_cmd_len; +- SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; +- SCpnt->underflow = SCpnt->old_underflow; ++ int tries = 0; + +- scsi_send_eh_cmnd(SCpnt, SCpnt->timeout_per_command); ++ do { ++ scsi_setup_cmd_retry(SCpnt); ++ scsi_send_eh_cmnd(SCpnt, SCpnt->timeout_per_command); ++ } while (SCpnt->eh_state == NEEDS_RETRY && tries++ < SCpnt->allowed); + + /* + * Hey, we are done. Let's look to see what happened. +@@ -409,16 +414,10 @@ + static unsigned char generic_sense[6] = + {REQUEST_SENSE, 0, 0, 0, 255, 0}; + unsigned char scsi_result0[256], *scsi_result = NULL; +- int saved_result; ++ int saved_result, tries; + + ASSERT_LOCK(&io_request_lock, 0); + +- memcpy((void *) SCpnt->cmnd, (void *) generic_sense, +- sizeof(generic_sense)); +- +- if (SCpnt->device->scsi_level <= SCSI_2) +- SCpnt->cmnd[1] = SCpnt->lun << 5; +- + scsi_result = (!SCpnt->host->hostt->unchecked_isa_dma) + ? &scsi_result0[0] : kmalloc(512, GFP_ATOMIC | GFP_DMA); + +@@ -426,24 +425,41 @@ + printk("cannot allocate scsi_result in scsi_request_sense.\n"); + return FAILED; + } +- /* +- * Zero the sense buffer. Some host adapters automatically always request +- * sense, so it is not a good idea that SCpnt->request_buffer and +- * SCpnt->sense_buffer point to the same address (DB). +- * 0 is not a valid sense code. +- */ +- memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); +- memset((void *) scsi_result, 0, 256); + + saved_result = SCpnt->result; +- SCpnt->request_buffer = scsi_result; +- SCpnt->request_bufflen = 256; +- SCpnt->use_sg = 0; +- SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); +- SCpnt->sc_data_direction = SCSI_DATA_READ; +- SCpnt->underflow = 0; + +- scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); ++ tries = 0; ++ do { ++ memcpy((void *) SCpnt->cmnd, (void *) generic_sense, ++ sizeof(generic_sense)); ++ ++ if (SCpnt->device->scsi_level <= SCSI_2) ++ SCpnt->cmnd[1] = SCpnt->lun << 5; ++ ++ /* ++ * Zero the sense buffer. Some host adapters automatically ++ * always request sense, so it is not a good idea that ++ * SCpnt->request_buffer and SCpnt->sense_buffer point to ++ * the same address (DB). 0 is not a valid sense code. ++ */ ++ memset((void *) SCpnt->sense_buffer, 0, ++ sizeof(SCpnt->sense_buffer)); ++ memset((void *) scsi_result, 0, 256); ++ ++ SCpnt->request_buffer = scsi_result; ++ SCpnt->request_bufflen = 256; ++ SCpnt->use_sg = 0; ++ SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); ++ SCpnt->sc_data_direction = SCSI_DATA_READ; ++ SCpnt->underflow = 0; ++ ++ scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); ++ /* ++ * If the SCSI device responded with "logical unit ++ * is in process of becoming ready", we need to ++ * retry this command. ++ */ ++ } while (SCpnt->eh_state == NEEDS_RETRY && tries++ < SENSE_RETRIES); + + /* Last chance to have valid sense data */ + if (!scsi_sense_valid(SCpnt)) +@@ -458,15 +474,8 @@ + * When we eventually call scsi_finish, we really wish to complete + * the original request, so let's restore the original data. (DB) + */ +- memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, +- sizeof(SCpnt->data_cmnd)); + SCpnt->result = saved_result; +- SCpnt->request_buffer = SCpnt->buffer; +- SCpnt->request_bufflen = SCpnt->bufflen; +- SCpnt->use_sg = SCpnt->old_use_sg; +- SCpnt->cmd_len = SCpnt->old_cmd_len; +- SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; +- SCpnt->underflow = SCpnt->old_underflow; ++ scsi_setup_cmd_retry(SCpnt); + + /* + * Hey, we are done. Let's look to see what happened. +@@ -484,40 +493,42 @@ + { + static unsigned char tur_command[6] = + {TEST_UNIT_READY, 0, 0, 0, 0, 0}; ++ int tries = 0; + +- memcpy((void *) SCpnt->cmnd, (void *) tur_command, +- sizeof(tur_command)); ++ do { ++ memcpy((void *) SCpnt->cmnd, (void *) tur_command, ++ sizeof(tur_command)); + +- if (SCpnt->device->scsi_level <= SCSI_2) +- SCpnt->cmnd[1] = SCpnt->lun << 5; ++ if (SCpnt->device->scsi_level <= SCSI_2) ++ SCpnt->cmnd[1] = SCpnt->lun << 5; + +- /* +- * Zero the sense buffer. The SCSI spec mandates that any +- * untransferred sense data should be interpreted as being zero. +- */ +- memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); ++ /* ++ * Zero the sense buffer. The SCSI spec mandates that any ++ * untransferred sense data should be interpreted as being zero. ++ */ ++ memset((void *) SCpnt->sense_buffer, 0, ++ sizeof(SCpnt->sense_buffer)); + +- SCpnt->request_buffer = NULL; +- SCpnt->request_bufflen = 0; +- SCpnt->use_sg = 0; +- SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); +- SCpnt->underflow = 0; +- SCpnt->sc_data_direction = SCSI_DATA_NONE; ++ SCpnt->request_buffer = NULL; ++ SCpnt->request_bufflen = 0; ++ SCpnt->use_sg = 0; ++ SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); ++ SCpnt->underflow = 0; ++ SCpnt->sc_data_direction = SCSI_DATA_NONE; + +- scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); ++ scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); ++ /* ++ * If the SCSI device responded with "logical unit ++ * is in process of becoming ready", we need to ++ * retry this command. ++ */ ++ } while (SCpnt->eh_state == NEEDS_RETRY && tries++ < TUR_RETRIES); + + /* + * When we eventually call scsi_finish, we really wish to complete + * the original request, so let's restore the original data. (DB) + */ +- memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, +- sizeof(SCpnt->data_cmnd)); +- SCpnt->request_buffer = SCpnt->buffer; +- SCpnt->request_bufflen = SCpnt->bufflen; +- SCpnt->use_sg = SCpnt->old_use_sg; +- SCpnt->cmd_len = SCpnt->old_cmd_len; +- SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; +- SCpnt->underflow = SCpnt->old_underflow; ++ scsi_setup_cmd_retry(SCpnt); + + /* + * Hey, we are done. Let's look to see what happened. +@@ -577,7 +588,6 @@ + + host = SCpnt->host; + +- retry: + /* + * We will use a queued command if possible, otherwise we will emulate the + * queuing and calling of completion function ourselves. +@@ -660,14 +670,13 @@ + SCSI_LOG_ERROR_RECOVERY(3, + printk("scsi_send_eh_cmnd: scsi_eh_completed_normally %x\n", ret)); + switch (ret) { +- case SUCCESS: +- SCpnt->eh_state = SUCCESS; +- break; +- case NEEDS_RETRY: +- goto retry; +- case FAILED: + default: +- SCpnt->eh_state = FAILED; ++ ret = FAILED; ++ /*FALLTHROUGH*/ ++ case FAILED: ++ case NEEDS_RETRY: ++ case SUCCESS: ++ SCpnt->eh_state = ret; + break; + } + } else { +@@ -1208,6 +1217,82 @@ + + + /* ++ * Function: scsi_eh_lock_done ++ * ++ * Purpose: free the command and request structures associated ++ * with the error handlers door lock request ++ * ++ * Arguments: SCpnt - the SCSI command block for the door lock request. ++ * ++ * Returns: Nothing ++ * ++ * Notes: We completed the asynchronous door lock request, and ++ * it has either locked the door or failed. We must free ++ * the command structures associated with this request. ++ */ ++static void scsi_eh_lock_done(struct scsi_cmnd *SCpnt) ++{ ++ struct scsi_request *SRpnt = SCpnt->sc_request; ++ ++ SCpnt->sc_request = NULL; ++ SRpnt->sr_command = NULL; ++ ++ scsi_release_command(SCpnt); ++ scsi_release_request(SRpnt); ++} ++ ++ ++/* ++ * Function: scsi_eh_lock_door ++ * ++ * Purpose: Prevent medium removal for the specified device ++ * ++ * Arguments: dev - SCSI device to prevent medium removal ++ * ++ * Locking: We must be called from process context; ++ * scsi_allocate_request() may sleep. ++ * ++ * Returns: Nothing ++ * ++ * Notes: We queue up an asynchronous "ALLOW MEDIUM REMOVAL" request ++ * on the head of the devices request queue, and continue. ++ * ++ * Bugs: scsi_allocate_request() may sleep waiting for existing ++ * requests to be processed. However, since we haven't ++ * kicked off any request processing for this host, this ++ * may deadlock. ++ * ++ * If scsi_allocate_request() fails for what ever reason, ++ * we completely forget to lock the door. ++ */ ++STATIC void scsi_eh_lock_door(struct scsi_device *dev) ++{ ++ struct scsi_request *SRpnt = scsi_allocate_request(dev); ++ ++ if (SRpnt == NULL) { ++ /* what now? */ ++ return; ++ } ++ ++ SRpnt->sr_cmnd[0] = ALLOW_MEDIUM_REMOVAL; ++ SRpnt->sr_cmnd[1] = (dev->scsi_level <= SCSI_2) ? (dev->lun << 5) : 0; ++ SRpnt->sr_cmnd[2] = 0; ++ SRpnt->sr_cmnd[3] = 0; ++ SRpnt->sr_cmnd[4] = SCSI_REMOVAL_PREVENT; ++ SRpnt->sr_cmnd[5] = 0; ++ SRpnt->sr_data_direction = SCSI_DATA_NONE; ++ SRpnt->sr_bufflen = 0; ++ SRpnt->sr_buffer = NULL; ++ SRpnt->sr_allowed = 5; ++ SRpnt->sr_done = scsi_eh_lock_done; ++ SRpnt->sr_timeout_per_command = 10 * HZ; ++ SRpnt->sr_cmd_len = COMMAND_SIZE(SRpnt->sr_cmnd[0]); ++ ++ scsi_insert_special_req(SRpnt, 1); ++} ++ ++ ++/* + * Function: scsi_restart_operations + * + * Purpose: Restart IO operations to the specified host. +@@ -1229,6 +1314,15 @@ + ASSERT_LOCK(&io_request_lock, 0); + + /* ++ * If the door was locked, we need to insert a door lock request ++ * onto the head of the SCSI request queue for the device. There ++ * is no point trying to lock the door of an off-line device. ++ */ ++ for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) ++ if (SDpnt->online && SDpnt->locked) ++ scsi_eh_lock_door(SDpnt); ++ ++ /* + * Next free up anything directly waiting upon the host. This will be + * requests for character device operations, and also for ioctls to queued + * block devices. +@@ -1248,8 +1342,7 @@ + request_queue_t *q; + if ((host->can_queue > 0 && (host->host_busy >= host->can_queue)) + || (host->host_blocked) +- || (host->host_self_blocked) +- || (SDpnt->device_blocked)) { ++ || (host->host_self_blocked)) { + break; + } + q = &SDpnt->request_queue; +@@ -1259,136 +1352,202 @@ + } + + /* +- * Function: scsi_unjam_host ++ * Function: scsi_eh_find_failed_command + * +- * Purpose: Attempt to fix a host which has a command that failed for +- * some reason. ++ * Purpose: Find a failed Scsi_Cmnd structure on a device. + * +- * Arguments: host - host that needs unjamming. +- * +- * Returns: Nothing ++ * Arguments: SDpnt - Scsi_Device structure + * +- * Notes: When we come in here, we *know* that all commands on the +- * bus have either completed, failed or timed out. We also +- * know that no further commands are being sent to the host, +- * so things are relatively quiet and we have freedom to +- * fiddle with things as we wish. ++ * Returns: Pointer to Scsi_Cmnd structure, or NULL on failure ++ */ ++STATIC Scsi_Cmnd *scsi_eh_find_failed_command(Scsi_Device *SDpnt) ++{ ++ Scsi_Cmnd *SCpnt; ++ ++ for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) ++ if (SCpnt->state == SCSI_STATE_FAILED || ++ SCpnt->state == SCSI_STATE_TIMEOUT) ++ return SCpnt; ++ ++ return NULL; ++} ++ ++ ++/* ++ * Function: scsi_eh_test_and_retry + * +- * Additional note: This is only the *default* implementation. It is possible +- * for individual drivers to supply their own version of this +- * function, and if the maintainer wishes to do this, it is +- * strongly suggested that this function be taken as a template +- * and modified. This function was designed to correctly handle +- * problems for about 95% of the different cases out there, and +- * it should always provide at least a reasonable amount of error +- * recovery. ++ * Purpose: Try to retry a failed command. + * +- * Note3: Any command marked 'FAILED' or 'TIMEOUT' must eventually +- * have scsi_finish_command() called for it. We do all of +- * the retry stuff here, so when we restart the host after we +- * return it should have an empty queue. ++ * Arguments: SCpnt - scsi command structure ++ * done - list of commands that have been successfully ++ * completed. ++ * ++ * Returns: SUCCESS or FAILED ++ * ++ * Note: If the TEST UNIT READY command successfully executes, ++ * but returns some form of "device not ready", we wait ++ * a while, and retry 3 times. The device could be still ++ * re-initialising. + */ +-STATIC int scsi_unjam_host(struct Scsi_Host *host) ++STATIC int scsi_eh_test_and_retry(Scsi_Cmnd *SCpnt, Scsi_Cmnd **done) + { +- int devices_failed; +- int numfailed; +- int ourrtn; +- int rtn = FALSE; +- int result; +- Scsi_Cmnd *SCloop; +- Scsi_Cmnd *SCpnt; +- Scsi_Device *SDpnt; +- Scsi_Device *SDloop; +- Scsi_Cmnd *SCdone; +- int timed_out; ++ int rtn, tries = 3; + +- ASSERT_LOCK(&io_request_lock, 0); ++ do { ++ rtn = scsi_test_unit_ready(SCpnt); ++ if (rtn != SUCCESS) ++ return rtn; + +- SCdone = NULL; ++ if (scsi_unit_is_ready(SCpnt)) ++ break; ++ ++ if (tries-- == 0) ++ return FAILED; ++ ++ scsi_sleep(5 * HZ); ++ } while (1); ++ ++ rtn = scsi_eh_retry_command(SCpnt); ++ if (rtn == SUCCESS) { ++ SCpnt->host->host_failed--; ++ scsi_eh_finish_command(done, SCpnt); ++ } ++ ++ return rtn; ++} + +- /* +- * First, protect against any sort of race condition. If any of the outstanding +- * commands are in states that indicate that we are not yet blocked (i.e. we are +- * not in a quiet state) then we got woken up in error. If we ever end up here, +- * we need to re-examine some of the assumptions. +- */ +- for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { +- for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { +- if (SCpnt->state == SCSI_STATE_FAILED +- || SCpnt->state == SCSI_STATE_TIMEOUT +- || SCpnt->state == SCSI_STATE_INITIALIZING +- || SCpnt->state == SCSI_STATE_UNUSED) { +- continue; +- } +- /* +- * Rats. Something is still floating around out there. This could +- * be the result of the fact that the upper level drivers are still frobbing +- * commands that might have succeeded. There are two outcomes. One is that +- * the command block will eventually be freed, and the other one is that +- * the command will be queued and will be finished along the way. +- */ +- SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler prematurely woken - commands still active (%p %x %d)\n", SCpnt, SCpnt->state, SCpnt->target)); + + /* +- * panic("SCSI Error handler woken too early\n"); ++ * Function: scsi_eh_restart_device + * +- * This is no longer a problem, since now the code cares only about +- * SCSI_STATE_TIMEOUT and SCSI_STATE_FAILED. +- * Other states are useful only to release active commands when devices are +- * set offline. If (host->host_active == host->host_busy) we can safely assume +- * that there are no commands in state other then TIMEOUT od FAILED. (DB) ++ * Purpose: Retry all failed or timed out commands for a device + * +- * FIXME: +- * It is not easy to release correctly commands according to their state when +- * devices are set offline, when the state is neither TIMEOUT nor FAILED. +- * When a device is set offline, we can have some command with +- * rq_status=RQ_SCSY_BUSY, owner=SCSI_STATE_HIGHLEVEL, +- * state=SCSI_STATE_INITIALIZING and the driver module cannot be released. +- * (DB, 17 May 1998) ++ * Arguments: SDpnt - SCSI device to retry ++ * done - list of commands that have been successfully ++ * completed. ++ * ++ * Returns: SUCCESS or failure code ++ */ ++STATIC int scsi_eh_restart_device(Scsi_Device *SDpnt, Scsi_Cmnd **done) ++{ ++ Scsi_Cmnd *SCpnt, *SCnext; ++ int rtn = SUCCESS; ++ ++ for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCnext) { ++ SCnext = SCpnt->next; ++ ++ if (SCpnt->state == SCSI_STATE_FAILED || ++ SCpnt->state == SCSI_STATE_TIMEOUT) { ++ rtn = scsi_eh_test_and_retry(SCpnt, done); ++ if (rtn != SUCCESS) ++ break; ++ } ++ } ++ ++ return rtn; ++} ++ ++/* ++ * Function: scsi_eh_set_device_offline ++ * ++ * Purpose: set a device off line ++ * ++ * Arguments: SDpnt - SCSI device to take off line ++ * done - list of commands that have been successfully ++ * completed. ++ * reason - text string describing why the device is off-line ++ * ++ * Returns: Nothing ++ * ++ * Notes: In addition, we complete each failed or timed out command ++ * attached to this device. + */ ++STATIC void scsi_eh_set_device_offline(Scsi_Device *SDpnt, Scsi_Cmnd **done, ++ const char *reason) ++{ ++ Scsi_Cmnd *SCpnt, *SCnext; ++ ++ printk(KERN_ERR "scsi: device set offline - %s: " ++ "host %d channel %d id %d lun %d\n", ++ reason, SDpnt->host->host_no, SDpnt->channel, ++ SDpnt->id, SDpnt->lun); ++ ++ SDpnt->online = FALSE; ++ ++ for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCnext) { ++ SCnext = SCpnt->next; ++ ++ switch (SCpnt->state) { ++ case SCSI_STATE_TIMEOUT: ++ SCpnt->result |= DRIVER_TIMEOUT; ++ /*FALLTHROUGH*/ ++ ++ case SCSI_STATE_FAILED: ++ SCSI_LOG_ERROR_RECOVERY(3, ++ printk("Finishing command for device %d %x\n", ++ SDpnt->id, SCpnt->result)); ++ ++ SDpnt->host->host_failed--; ++ scsi_eh_finish_command(done, SCpnt); ++ break; ++ ++ default: ++ break; + } + } ++} ++ ++static void scsi_unjam_request_sense(struct Scsi_Host *host, Scsi_Cmnd **done) ++{ ++ int rtn; ++ int result; ++ Scsi_Cmnd *SCpnt; ++ Scsi_Device *SDpnt; + + /* + * Next, see if we need to request sense information. if so, + * then get it now, so we have a better idea of what to do. +- * FIXME(eric) this has the unfortunate side effect that if a host +- * adapter does not automatically request sense information, that we end +- * up shutting it down before we request it. All hosts should be doing this +- * anyways, so for now all I have to say is tough noogies if you end up in here. +- * On second thought, this is probably a good idea. We *really* want to give +- * authors an incentive to automatically request this. ++ * FIXME(eric) this has the unfortunate side effect that if a ++ * host adapter does not automatically request sense information, ++ * that we end up shutting it down before we request it. All ++ * hosts should be doing this anyways, so for now all I have ++ * to say is tough noogies if you end up in here. On second ++ * thought, this is probably a good idea. We *really* want ++ * to give authors an incentive to automatically request this. + */ +- SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Checking to see if we need to request sense\n")); ++ SCSI_LOG_ERROR_RECOVERY(3, ++ printk("scsi_unjam_host: Checking to see if we need to request sense\n")); + + for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { + for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { +- if (SCpnt->state != SCSI_STATE_FAILED || scsi_sense_valid(SCpnt)) { ++ if (SCpnt->state != SCSI_STATE_FAILED || scsi_sense_valid(SCpnt)) + continue; +- } +- SCSI_LOG_ERROR_RECOVERY(2, printk("scsi_unjam_host: Requesting sense for %d\n", +- SCpnt->target)); ++ ++ SCSI_LOG_ERROR_RECOVERY(2, ++ printk("scsi_unjam_host: Requesting sense for %d\n", ++ SCpnt->target)); + rtn = scsi_request_sense(SCpnt); +- if (rtn != SUCCESS) { ++ if (rtn != SUCCESS) + continue; +- } +- SCSI_LOG_ERROR_RECOVERY(3, printk("Sense requested for %p - result %x\n", +- SCpnt, SCpnt->result)); ++ ++ SCSI_LOG_ERROR_RECOVERY(3, ++ printk("Sense requested for %p - result %x\n", ++ SCpnt, SCpnt->result)); + SCSI_LOG_ERROR_RECOVERY(3, print_sense("bh", SCpnt)); + + result = scsi_decide_disposition(SCpnt); + + /* +- * If the result was normal, then just pass it along to the +- * upper level. ++ * If the result was normal, then just pass ++ * it along to the upper level. + */ + if (result == SUCCESS) { + SCpnt->host->host_failed--; +- scsi_eh_finish_command(&SCdone, SCpnt); ++ scsi_eh_finish_command(done, SCpnt); + } +- if (result != NEEDS_RETRY) { ++ if (result != NEEDS_RETRY) + continue; +- } ++ + /* + * We only come in here if we want to retry a + * command. The test to see whether the command +@@ -1398,20 +1557,29 @@ + */ + SCpnt->state = NEEDS_RETRY; + rtn = scsi_eh_retry_command(SCpnt); +- if (rtn != SUCCESS) { ++ if (rtn != SUCCESS) + continue; +- } ++ + /* + * We eventually hand this one back to the top level. + */ + SCpnt->host->host_failed--; +- scsi_eh_finish_command(&SCdone, SCpnt); ++ scsi_eh_finish_command(done, SCpnt); + } + } ++} ++ ++static void scsi_unjam_count(struct Scsi_Host *host, Scsi_Cmnd **done) ++{ ++ Scsi_Device *SDpnt; ++ Scsi_Cmnd *SCpnt; ++ int devices_failed; ++ int numfailed; ++ int timed_out; + + /* +- * Go through the list of commands and figure out where we stand and how bad things +- * really are. ++ * Go through the list of commands and figure out where we ++ * stand and how bad things really are. + */ + numfailed = 0; + timed_out = 0; +@@ -1421,359 +1589,478 @@ + + for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { + if (SCpnt->state == SCSI_STATE_FAILED) { +- SCSI_LOG_ERROR_RECOVERY(5, printk("Command to ID %d failed\n", +- SCpnt->target)); ++ SCSI_LOG_ERROR_RECOVERY(5, ++ printk("Command to ID %d failed\n", ++ SCpnt->target)); + numfailed++; + device_error++; + } + if (SCpnt->state == SCSI_STATE_TIMEOUT) { +- SCSI_LOG_ERROR_RECOVERY(5, printk("Command to ID %d timedout\n", +- SCpnt->target)); ++ SCSI_LOG_ERROR_RECOVERY(5, ++ printk("Command to ID %d timedout\n", ++ SCpnt->target)); + timed_out++; + device_error++; + } + } +- if (device_error > 0) { ++ if (device_error > 0) + devices_failed++; +- } + } + +- SCSI_LOG_ERROR_RECOVERY(2, printk("Total of %d+%d commands on %d devices require eh work\n", +- numfailed, timed_out, devices_failed)); ++ SCSI_LOG_ERROR_RECOVERY(2, ++ printk("Total of %d+%d commands on %d devices require eh work\n", ++ numfailed, timed_out, devices_failed)); ++} ++ ++static void scsi_unjam_abort(struct Scsi_Host *host, Scsi_Cmnd **done) ++{ ++ Scsi_Device *SDpnt; ++ Scsi_Cmnd *SCpnt; ++ int rtn; + +- if (host->host_failed == 0) { +- ourrtn = TRUE; +- goto leave; +- } + /* +- * Next, try and see whether or not it makes sense to try and abort +- * the running command. This only works out to be the case if we have +- * one command that has timed out. If the command simply failed, it +- * makes no sense to try and abort the command, since as far as the +- * host adapter is concerned, it isn't running. ++ * Next, try and see whether or not it makes sense to try and ++ * abort the running command. This only works out to be the ++ * case if we have one command that has timed out. If the ++ * command simply failed, it makes no sense to try and abort ++ * the command, since as far as the host adapter is concerned, ++ * it isn't running. + */ + +- SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Checking to see if we want to try abort\n")); ++ SCSI_LOG_ERROR_RECOVERY(3, ++ printk("scsi_unjam_host: Checking to see if we want to try abort\n")); + + for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { +- for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) { +- if (SCloop->state != SCSI_STATE_TIMEOUT) { ++ for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { ++ if (SCpnt->state != SCSI_STATE_TIMEOUT) + continue; +- } +- rtn = scsi_try_to_abort_command(SCloop, ABORT_TIMEOUT); +- if (rtn == SUCCESS) { +- rtn = scsi_test_unit_ready(SCloop); +- +- if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) { +- rtn = scsi_eh_retry_command(SCloop); +- +- if (rtn == SUCCESS) { +- SCloop->host->host_failed--; +- scsi_eh_finish_command(&SCdone, SCloop); +- } +- } +- } ++ ++ rtn = scsi_try_to_abort_command(SCpnt, ABORT_TIMEOUT); ++ if (rtn == SUCCESS) ++ scsi_eh_test_and_retry(SCpnt, done); + } + } ++} ++ ++static void scsi_unjam_device_reset(struct Scsi_Host *host, Scsi_Cmnd **done) ++{ ++ Scsi_Device *SDpnt; ++ Scsi_Cmnd *SCpnt; ++ int rtn; + +- /* +- * If we have corrected all of the problems, then we are done. +- */ +- if (host->host_failed == 0) { +- ourrtn = TRUE; +- goto leave; +- } + /* + * Either the abort wasn't appropriate, or it didn't succeed. +- * Now try a bus device reset. Still, look to see whether we have +- * multiple devices that are jammed or not - if we have multiple devices, +- * it makes no sense to try BUS_DEVICE_RESET - we really would need +- * to try a BUS_RESET instead. ++ * Now try a bus device reset. Still, look to see whether we ++ * have multiple devices that are jammed or not - if we have ++ * multiple devices, it makes no sense to try BUS_DEVICE_RESET ++ * - we really would need to try a BUS_RESET instead. + * +- * Does this make sense - should we try BDR on each device individually? +- * Yes, definitely. ++ * Does this make sense - should we try BDR on each device ++ * individually? Yes, definitely. + */ +- SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Checking to see if we want to try BDR\n")); ++ SCSI_LOG_ERROR_RECOVERY(3, ++ printk("scsi_unjam_host: Checking to see if we want to try BDR\n")); + + for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { +- for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) { +- if (SCloop->state == SCSI_STATE_FAILED +- || SCloop->state == SCSI_STATE_TIMEOUT) { +- break; +- } +- } +- +- if (SCloop == NULL) { ++ SCpnt = scsi_eh_find_failed_command(SDpnt); ++ if (SCpnt == NULL) + continue; +- } ++ + /* +- * OK, we have a device that is having problems. Try and send +- * a bus device reset to it. ++ * OK, we have a device that is having problems. ++ * Try and send a bus device reset to it. ++ */ ++ rtn = scsi_try_bus_device_reset(SCpnt, RESET_TIMEOUT); ++ ++ /* ++ * A successful bus device reset causes all commands ++ * currently executing on the device to terminate. ++ * We expect the HBA driver to "forget" all commands ++ * associated with this device. + * +- * FIXME(eric) - make sure we handle the case where multiple +- * commands to the same device have failed. They all must +- * get properly restarted. +- */ +- rtn = scsi_try_bus_device_reset(SCloop, RESET_TIMEOUT); +- +- if (rtn == SUCCESS) { +- rtn = scsi_test_unit_ready(SCloop); +- +- if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) { +- rtn = scsi_eh_retry_command(SCloop); +- +- if (rtn == SUCCESS) { +- SCloop->host->host_failed--; +- scsi_eh_finish_command(&SCdone, SCloop); +- } +- } +- } ++ * Retry each failed or timed out command currently ++ * outstanding for this device. ++ * ++ * If any command fails, bail out. We will try a ++ * bus reset instead. ++ */ ++ if (rtn == SUCCESS) ++ scsi_eh_restart_device(SDpnt, done); + } ++} ++ ++static void scsi_unjam_bus_reset(struct Scsi_Host *host, Scsi_Cmnd **done) ++{ ++ Scsi_Device *SDpnt; ++ Scsi_Cmnd *SCpnt; ++ int rtn, channel, max_channel = 0; + +- if (host->host_failed == 0) { +- ourrtn = TRUE; +- goto leave; +- } + /* +- * If we ended up here, we have serious problems. The only thing left +- * to try is a full bus reset. If someone has grabbed the bus and isn't +- * letting go, then perhaps this will help. ++ * If we ended up here, we have serious problems. The only thing ++ * left to try is a full bus reset. If someone has grabbed the ++ * bus and isn't letting go, then perhaps this will help. + */ +- SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Try hard bus reset\n")); ++ SCSI_LOG_ERROR_RECOVERY(3, ++ printk("scsi_unjam_host: Try hard bus reset\n")); + +- /* +- * We really want to loop over the various channels, and do this on +- * a channel by channel basis. We should also check to see if any +- * of the failed commands are on soft_reset devices, and if so, skip +- * the reset. ++ /* ++ * Find the maximum channel number for this host. + */ +- for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { +- next_device: +- for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { +- if (SCpnt->state != SCSI_STATE_FAILED +- && SCpnt->state != SCSI_STATE_TIMEOUT) { ++ for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) ++ if (SDpnt->channel > max_channel) ++ max_channel = SDpnt->channel; ++ ++ /* ++ * Loop over each channel, and see if it any device on ++ * each channel has failed. ++ */ ++ for (channel = 0; channel <= max_channel; channel++) { ++ Scsi_Cmnd *failed_command; ++ int soft_reset; ++ ++ try_again: ++ failed_command = NULL; ++ soft_reset = 0; ++ ++ /* ++ * Loop over each device on this channel locating any ++ * failed command. We need a Scsi_Cmnd structure to ++ * call the bus reset function. ++ * ++ * We also need to check if any of the failed commands ++ * are on soft_reset devices, and if so, skip the reset. ++ */ ++ for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { ++ if (SDpnt->channel != channel) + continue; +- } +- /* +- * We have a failed command. Make sure there are no other failed +- * commands on the same channel that are timed out and implement a +- * soft reset. +- */ +- for (SDloop = host->host_queue; SDloop; SDloop = SDloop->next) { +- for (SCloop = SDloop->device_queue; SCloop; SCloop = SCloop->next) { +- if (SCloop->channel != SCpnt->channel) { +- continue; +- } +- if (SCloop->state != SCSI_STATE_FAILED +- && SCloop->state != SCSI_STATE_TIMEOUT) { +- continue; +- } +- if (SDloop->soft_reset && SCloop->state == SCSI_STATE_TIMEOUT) { +- /* +- * If this device uses the soft reset option, and this +- * is one of the devices acting up, then our only +- * option is to wait a bit, since the command is +- * supposedly still running. +- * +- * FIXME(eric) - right now we will just end up falling +- * through to the 'take device offline' case. +- * +- * FIXME(eric) - It is possible that the command completed +- * *after* the error recovery procedure started, and if this +- * is the case, we are worrying about nothing here. +- */ +- +- scsi_sleep(1 * HZ); +- goto next_device; +- } +- } +- } ++ ++ SCpnt = scsi_eh_find_failed_command(SDpnt); ++ if (SCpnt) ++ failed_command = SCpnt; + + /* +- * We now know that we are able to perform a reset for the +- * bus that SCpnt points to. There are no soft-reset devices +- * with outstanding timed out commands. ++ * If this device has timed out or failed commands, ++ * and uses the soft_reset option. + */ +- rtn = scsi_try_bus_reset(SCpnt); +- if (rtn == SUCCESS) { +- for (SDloop = host->host_queue; SDloop; SDloop = SDloop->next) { +- for (SCloop = SDloop->device_queue; SCloop; SCloop = SCloop->next) { +- if (SCloop->channel != SCpnt->channel) { +- continue; +- } +- if (SCloop->state != SCSI_STATE_FAILED +- && SCloop->state != SCSI_STATE_TIMEOUT) { +- continue; +- } +- rtn = scsi_test_unit_ready(SCloop); +- +- if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) { +- rtn = scsi_eh_retry_command(SCloop); +- +- if (rtn == SUCCESS) { +- SCpnt->host->host_failed--; +- scsi_eh_finish_command(&SCdone, SCloop); +- } +- } +- /* +- * If the bus reset worked, but we are still unable to +- * talk to the device, take it offline. +- * FIXME(eric) - is this really the correct thing to do? +- */ +- if (rtn != SUCCESS) { +- printk(KERN_INFO "scsi: device set offline - not ready or command retry failed after bus reset: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun); +- +- SDloop->online = FALSE; +- SDloop->host->host_failed--; +- scsi_eh_finish_command(&SCdone, SCloop); +- } +- } +- } ++ if (SCpnt && SDpnt->soft_reset) ++ soft_reset = 1; ++ } ++ ++ /* ++ * If this channel hasn't failed, we ++ * don't need to reset it. ++ */ ++ if (!failed_command) ++ continue; ++ ++ /* ++ * If this device uses the soft reset option, and this ++ * is one of the devices acting up, then our only ++ * option is to wait a bit, since the command is ++ * supposedly still running. ++ * ++ * FIXME(eric) - right now we will just end up falling ++ * through to the 'take device offline' case. ++ * ++ * FIXME(eric) - It is possible that the command completed ++ * *after* the error recovery procedure started, and if ++ * this is the case, we are worrying about nothing here. ++ * ++ * FIXME(rmk) - This should be bounded; we shouldn't wait ++ * for an infinite amount of time for any device. ++ */ ++ if (soft_reset) { ++ SCSI_LOG_ERROR_RECOVERY(3, ++ printk("scsi_unjam_host: unable to try bus " ++ "reset for host %d channel %d\n", ++ host->host_no, channel)); ++ scsi_sleep(1 * HZ); ++ goto try_again; ++ } ++ ++ /* ++ * We now know that we are able to perform a reset for the ++ * bus that SCpnt points to. There are no soft-reset devices ++ * with outstanding timed out commands. ++ */ ++ rtn = scsi_try_bus_reset(failed_command); ++ ++ /* ++ * If we failed to reset the bus, move on to the next bus. ++ */ ++ if (rtn != SUCCESS) ++ continue; ++ ++ /* ++ * We succeeded. Retry each failed command. ++ */ ++ for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { ++ if (SDpnt->channel != channel) ++ continue; ++ ++ rtn = scsi_eh_restart_device(SDpnt, done); ++ ++ if (rtn != SUCCESS) { ++ SCpnt = scsi_eh_find_failed_command(SDpnt); ++ ++ /* ++ * This device failed again. Since a bus ++ * reset freed it up, chances are we've ++ * hit the same problem, so try the same ++ * solution. We also need to ensure that ++ * the SCSI bus is in the BUS FREE state ++ * so we can try to talk to other devices. ++ */ ++ scsi_try_bus_reset(SCpnt); ++ scsi_eh_set_device_offline(SDpnt, done, ++ "not ready or command retry " ++ "failed after bus reset"); + } + } + } ++} ++ ++static void scsi_unjam_host_reset(struct Scsi_Host *host, Scsi_Cmnd **done) ++{ ++ Scsi_Device *SDpnt; ++ Scsi_Cmnd *SCpnt; ++ Scsi_Cmnd *failed_command = NULL; ++ int rtn, soft_reset; + +- if (host->host_failed == 0) { +- ourrtn = TRUE; +- goto leave; +- } + /* +- * If we ended up here, we have serious problems. The only thing left +- * to try is a full host reset - perhaps the firmware on the device +- * crashed, or something like that. ++ * If we ended up here, we have serious problems. The only thing ++ * left to try is a full host reset - perhaps the firmware on the ++ * device crashed, or something like that. + * +- * It is assumed that a succesful host reset will cause *all* information +- * about the command to be flushed from both the host adapter *and* the +- * device. ++ * It is assumed that a succesful host reset will cause *all* ++ * information about the command to be flushed from both the host ++ * adapter *and* the device. + * +- * FIXME(eric) - it isn't clear that devices that implement the soft reset +- * option can ever be cleared except via cycling the power. The problem is +- * that sending the host reset command will cause the host to forget +- * about the pending command, but the device won't forget. For now, we +- * skip the host reset option if any of the failed devices are configured +- * to use the soft reset option. ++ * FIXME(eric) - it isn't clear that devices that implement the ++ * soft reset option can ever be cleared except via cycling the ++ * power. The problem is that sending the host reset command will ++ * cause the host to forget about the pending command, but the ++ * device won't forget. For now, we skip the host reset option ++ * if any of the failed devices are configured to use the soft ++ * reset option. + */ ++ SCSI_LOG_ERROR_RECOVERY(3, ++ printk("scsi_unjam_host: Try host reset\n")); ++ ++ try_again: ++ failed_command = NULL; ++ soft_reset = 0; ++ + for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { +- next_device2: +- for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { +- if (SCpnt->state != SCSI_STATE_FAILED +- && SCpnt->state != SCSI_STATE_TIMEOUT) { +- continue; +- } +- if (SDpnt->soft_reset && SCpnt->state == SCSI_STATE_TIMEOUT) { +- /* +- * If this device uses the soft reset option, and this +- * is one of the devices acting up, then our only +- * option is to wait a bit, since the command is +- * supposedly still running. +- * +- * FIXME(eric) - right now we will just end up falling +- * through to the 'take device offline' case. +- */ +- SCSI_LOG_ERROR_RECOVERY(3, +- printk("scsi_unjam_host: Unable to try hard host reset\n")); ++ /* ++ * Locate any failed commands for this device. ++ */ ++ SCpnt = scsi_eh_find_failed_command(SDpnt); ++ if (SCpnt) ++ failed_command = SCpnt; + +- /* +- * Due to the spinlock, we will never get out of this +- * loop without a proper wait. (DB) +- */ +- scsi_sleep(1 * HZ); ++ /* ++ * If this device has timed out or failed commands, ++ * and uses the soft_reset option. ++ */ ++ if (SCpnt && SDpnt->soft_reset) ++ soft_reset = 1; ++ } + +- goto next_device2; +- } +- SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Try hard host reset\n")); ++ /* ++ * If this device uses the soft reset option, and this ++ * is one of the devices acting up, then our only ++ * option is to wait a bit, since the command is ++ * supposedly still running. ++ * ++ * FIXME(eric) - right now we will just end up falling ++ * through to the 'take device offline' case. ++ * ++ * FIXME(rmk) - This should be bounded; we shouldn't wait ++ * for an infinite amount of time for any device. ++ */ ++ if (soft_reset) { ++ SCSI_LOG_ERROR_RECOVERY(3, ++ printk("scsi_unjam_host: unable to try " ++ "hard host reset\n")); + + /* +- * FIXME(eric) - we need to obtain a valid SCpnt to perform this call. ++ * Due to the spinlock, we will never get out of this ++ * loop without a proper wait. (DB) + */ +- rtn = scsi_try_host_reset(SCpnt); +- if (rtn == SUCCESS) { +- /* +- * FIXME(eric) we assume that all commands are flushed from the +- * controller. We should get a DID_RESET for all of the commands +- * that were pending. We should ignore these so that we can +- * guarantee that we are in a consistent state. +- * +- * I believe this to be the case right now, but this needs to be +- * tested. +- */ +- for (SDloop = host->host_queue; SDloop; SDloop = SDloop->next) { +- for (SCloop = SDloop->device_queue; SCloop; SCloop = SCloop->next) { +- if (SCloop->state != SCSI_STATE_FAILED +- && SCloop->state != SCSI_STATE_TIMEOUT) { +- continue; +- } +- rtn = scsi_test_unit_ready(SCloop); +- +- if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) { +- rtn = scsi_eh_retry_command(SCloop); +- +- if (rtn == SUCCESS) { +- SCpnt->host->host_failed--; +- scsi_eh_finish_command(&SCdone, SCloop); +- } +- } +- if (rtn != SUCCESS) { +- printk(KERN_INFO "scsi: device set offline - not ready or command retry failed after host reset: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun); +- SDloop->online = FALSE; +- SDloop->host->host_failed--; +- scsi_eh_finish_command(&SCdone, SCloop); +- } +- } +- } +- } +- } +- } ++ scsi_sleep(1 * HZ); + +- /* +- * If we solved all of the problems, then let's rev up the engines again. +- */ +- if (host->host_failed == 0) { +- ourrtn = TRUE; +- goto leave; ++ goto try_again; + } ++ ++ SCSI_LOG_ERROR_RECOVERY(3, ++ printk("scsi_unjam_host: Try hard host reset\n")); ++ + /* +- * If the HOST RESET failed, then for now we assume that the entire host +- * adapter is too hosed to be of any use. For our purposes, however, it is +- * easier to simply take the devices offline that correspond to commands +- * that failed. ++ * FIXME(eric) - we need to obtain a valid SCpnt to perform this call. + */ +- SCSI_LOG_ERROR_RECOVERY(1, printk("scsi_unjam_host: Take device offline\n")); ++ rtn = scsi_try_host_reset(failed_command); ++ if (rtn == SUCCESS) { ++ /* ++ * FIXME(eric) we assume that all commands are flushed from ++ * the controller. We should get a DID_RESET for all of the ++ * commands that were pending. We should ignore these so ++ * that we can guarantee that we are in a consistent state. ++ * ++ * I believe this to be the case right now, but this needs ++ * to be tested. ++ */ ++ for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { ++ rtn = scsi_eh_restart_device(SDpnt, done); + +- for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { +- for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) { +- if (SCloop->state == SCSI_STATE_FAILED || SCloop->state == SCSI_STATE_TIMEOUT) { +- SDloop = SCloop->device; +- if (SDloop->online == TRUE) { +- printk(KERN_INFO "scsi: device set offline - command error recover failed: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun); +- SDloop->online = FALSE; +- } ++ if (rtn != SUCCESS) { ++ SCpnt = scsi_eh_find_failed_command(SDpnt); + + /* +- * This should pass the failure up to the top level driver, and +- * it will have to try and do something intelligent with it. ++ * This device failed again. Since a host ++ * reset freed it up, chances are we've ++ * hit the same problem, so try the same ++ * solution. We also need to ensure that ++ * the SCSI bus is in the BUS FREE state ++ * so we can try to talk to other devices. + */ +- SCloop->host->host_failed--; +- +- if (SCloop->state == SCSI_STATE_TIMEOUT) { +- SCloop->result |= (DRIVER_TIMEOUT << 24); +- } +- SCSI_LOG_ERROR_RECOVERY(3, printk("Finishing command for device %d %x\n", +- SDloop->id, SCloop->result)); +- +- scsi_eh_finish_command(&SCdone, SCloop); ++ scsi_try_host_reset(SCpnt); ++ scsi_eh_set_device_offline(SDpnt, done, ++ "not ready or command retry " ++ "failed after host reset"); + } + } + } ++} ++ ++static void scsi_unjam_failure(struct Scsi_Host *host, Scsi_Cmnd **done) ++{ ++ Scsi_Device *SDpnt; + +- if (host->host_failed != 0) { ++ /* ++ * If the HOST RESET failed, then for now we assume that the ++ * entire host adapter is too hosed to be of any use. For our ++ * purposes, however, it is easier to simply take the devices ++ * offline that correspond to commands that failed. ++ */ ++ SCSI_LOG_ERROR_RECOVERY(1, ++ printk("scsi_unjam_host: Take device offline\n")); ++ ++ for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) ++ scsi_eh_set_device_offline(SDpnt, done, ++ "command error recover failed"); ++ ++ if (host->host_failed != 0) + panic("scsi_unjam_host: Miscount of number of failed commands.\n"); +- } ++ + SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Returning\n")); ++} ++ ++static void (*unjam_method[])(struct Scsi_Host *, Scsi_Cmnd **) = { ++ scsi_unjam_request_sense, ++ scsi_unjam_count, ++ scsi_unjam_abort, ++ scsi_unjam_device_reset, ++ scsi_unjam_bus_reset, ++ scsi_unjam_host_reset, ++ scsi_unjam_failure, ++}; ++ ++/* ++ * Function: scsi_unjam_host ++ * ++ * Purpose: Attempt to fix a host which has a command that failed for ++ * some reason. ++ * ++ * Arguments: host - host that needs unjamming. ++ * ++ * Returns: Nothing ++ * ++ * Notes: When we come in here, we *know* that all commands on the ++ * bus have either completed, failed or timed out. We also ++ * know that no further commands are being sent to the host, ++ * so things are relatively quiet and we have freedom to ++ * fiddle with things as we wish. ++ * ++ * Additional note: This is only the *default* implementation. It is possible ++ * for individual drivers to supply their own version of this ++ * function, and if the maintainer wishes to do this, it is ++ * strongly suggested that this function be taken as a template ++ * and modified. This function was designed to correctly handle ++ * problems for about 95% of the different cases out there, and ++ * it should always provide at least a reasonable amount of error ++ * recovery. ++ * ++ * Note3: Any command marked 'FAILED' or 'TIMEOUT' must eventually ++ * have scsi_finish_command() called for it. We do all of ++ * the retry stuff here, so when we restart the host after we ++ * return it should have an empty queue. ++ */ ++STATIC int scsi_unjam_host(struct Scsi_Host *host) ++{ ++ Scsi_Cmnd *SCdone = NULL; ++ Scsi_Cmnd *SCpnt; ++ Scsi_Device *SDpnt; ++ int ourrtn = FALSE; ++ int i; + +- ourrtn = FALSE; ++ ASSERT_LOCK(&io_request_lock, 0); + +- leave: ++ /* ++ * First, protect against any sort of race condition. If any of the outstanding ++ * commands are in states that indicate that we are not yet blocked (i.e. we are ++ * not in a quiet state) then we got woken up in error. If we ever end up here, ++ * we need to re-examine some of the assumptions. ++ */ ++ for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) { ++ for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) { ++ if (SCpnt->state == SCSI_STATE_FAILED ++ || SCpnt->state == SCSI_STATE_TIMEOUT ++ || SCpnt->state == SCSI_STATE_INITIALIZING ++ || SCpnt->state == SCSI_STATE_UNUSED) { ++ continue; ++ } ++ /* ++ * Rats. Something is still floating around out there. This could ++ * be the result of the fact that the upper level drivers are still frobbing ++ * commands that might have succeeded. There are two outcomes. One is that ++ * the command block will eventually be freed, and the other one is that ++ * the command will be queued and will be finished along the way. ++ */ ++ SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler prematurely woken - commands still active (%p %x %d)\n", SCpnt, SCpnt->state, SCpnt->target)); ++ ++/* ++ * panic("SCSI Error handler woken too early\n"); ++ * ++ * This is no longer a problem, since now the code cares only about ++ * SCSI_STATE_TIMEOUT and SCSI_STATE_FAILED. ++ * Other states are useful only to release active commands when devices are ++ * set offline. If (host->host_active == host->host_busy) we can safely assume ++ * that there are no commands in state other then TIMEOUT od FAILED. (DB) ++ * ++ * FIXME: ++ * It is not easy to release correctly commands according to their state when ++ * devices are set offline, when the state is neither TIMEOUT nor FAILED. ++ * When a device is set offline, we can have some command with ++ * rq_status=RQ_SCSY_BUSY, owner=SCSI_STATE_HIGHLEVEL, ++ * state=SCSI_STATE_INITIALIZING and the driver module cannot be released. ++ * (DB, 17 May 1998) ++ */ ++ } ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(unjam_method); i++) { ++ unjam_method[i](host, &SCdone); ++ ++ /* ++ * If we solved all of the problems, then ++ * let's rev up the engines again. ++ */ ++ if (host->host_failed == 0) { ++ ourrtn = TRUE; ++ break; ++ } ++ } + + /* + * We should have a list of commands that we 'finished' during the course of +@@ -2013,3 +2300,17 @@ + * tab-width: 8 + * End: + */ ++ ++EXPORT_SYMBOL(scsi_eh_times_out); ++EXPORT_SYMBOL(scsi_eh_retry_command); ++EXPORT_SYMBOL(scsi_request_sense); ++EXPORT_SYMBOL(scsi_test_unit_ready); ++EXPORT_SYMBOL(scsi_unit_is_ready); ++EXPORT_SYMBOL(scsi_eh_finish_command); ++EXPORT_SYMBOL(scsi_try_to_abort_command); ++EXPORT_SYMBOL(scsi_try_bus_device_reset); ++EXPORT_SYMBOL(scsi_try_bus_reset); ++EXPORT_SYMBOL(scsi_try_host_reset); ++EXPORT_SYMBOL(scsi_sense_valid); ++EXPORT_SYMBOL(scsi_done); ++EXPORT_SYMBOL(scsi_decide_disposition); +diff -urN linux-2.4.26/drivers/scsi/scsi_ioctl.c linux-2.4.26-vrs1/drivers/scsi/scsi_ioctl.c +--- linux-2.4.26/drivers/scsi/scsi_ioctl.c 2003-08-25 12:44:42.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/scsi/scsi_ioctl.c 2004-01-14 21:39:12.000000000 +0000 +@@ -153,6 +153,29 @@ + return result; + } + ++int scsi_set_medium_removal(Scsi_Device *dev, char state) ++{ ++ char scsi_cmd[MAX_COMMAND_SIZE]; ++ int ret; ++ ++ if (!dev->removable || !dev->lockable) ++ return 0; ++ ++ scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL; ++ scsi_cmd[1] = (dev->scsi_level <= SCSI_2) ? (dev->lun << 5) : 0; ++ scsi_cmd[2] = 0; ++ scsi_cmd[3] = 0; ++ scsi_cmd[4] = state; ++ scsi_cmd[5] = 0; ++ ++ ret = ioctl_internal_command(dev, scsi_cmd, IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES); ++ ++ if (ret == 0) ++ dev->locked = state == SCSI_REMOVAL_PREVENT; ++ ++ return ret; ++} ++ + /* + * This interface is depreciated - users should use the scsi generic (sg) + * interface instead, as this is a more flexible approach to performing +@@ -450,24 +473,9 @@ + return scsi_ioctl_send_command((Scsi_Device *) dev, + (Scsi_Ioctl_Command *) arg); + case SCSI_IOCTL_DOORLOCK: +- if (!dev->removable || !dev->lockable) +- return 0; +- scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL; +- scsi_cmd[1] = cmd_byte1; +- scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0; +- scsi_cmd[4] = SCSI_REMOVAL_PREVENT; +- return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd, +- IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES); +- break; ++ return scsi_set_medium_removal(dev, SCSI_REMOVAL_PREVENT); + case SCSI_IOCTL_DOORUNLOCK: +- if (!dev->removable || !dev->lockable) +- return 0; +- scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL; +- scsi_cmd[1] = cmd_byte1; +- scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0; +- scsi_cmd[4] = SCSI_REMOVAL_ALLOW; +- return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd, +- IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES); ++ return scsi_set_medium_removal(dev, SCSI_REMOVAL_ALLOW); + case SCSI_IOCTL_TEST_UNIT_READY: + scsi_cmd[0] = TEST_UNIT_READY; + scsi_cmd[1] = cmd_byte1; +diff -urN linux-2.4.26/drivers/scsi/scsi_lib.c linux-2.4.26-vrs1/drivers/scsi/scsi_lib.c +--- linux-2.4.26/drivers/scsi/scsi_lib.c 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/scsi/scsi_lib.c 2004-04-18 21:47:51.000000000 +0100 +@@ -208,6 +208,30 @@ + } + + /* ++ * Function: scsi_setup_cmd_retry() ++ * ++ * Purpose: Restore the command state for a retry ++ * ++ * Arguments: SCpnt - command to be restored ++ * ++ * Returns: Nothing ++ * ++ * Notes: Immediately prior to retrying a command, we need ++ * to restore certain fields that we saved above. ++ */ ++void scsi_setup_cmd_retry(Scsi_Cmnd *SCpnt) ++{ ++ memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd, ++ sizeof(SCpnt->data_cmnd)); ++ SCpnt->request_buffer = SCpnt->buffer; ++ SCpnt->request_bufflen = SCpnt->bufflen; ++ SCpnt->use_sg = SCpnt->old_use_sg; ++ SCpnt->cmd_len = SCpnt->old_cmd_len; ++ SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; ++ SCpnt->underflow = SCpnt->old_underflow; ++} ++ ++/* + * Function: scsi_queue_next_request() + * + * Purpose: Handle post-processing of completed commands. +@@ -731,7 +755,7 @@ + printk("scsi%d: ERROR on channel %d, id %d, lun %d, CDB: ", + SCpnt->host->host_no, (int) SCpnt->channel, + (int) SCpnt->target, (int) SCpnt->lun); +- print_command(SCpnt->cmnd); ++ print_command(SCpnt->data_cmnd); + print_sense("sd", SCpnt); + SCpnt = scsi_end_request(SCpnt, 0, block_sectors); + return; +@@ -906,8 +930,17 @@ + * space. Technically the error handling thread should be + * doing this crap, but the error handler isn't used by + * most hosts. ++ * ++ * (rmk) ++ * Trying to lock the door can cause deadlocks. We therefore ++ * only use this for old hosts; our door locking is now done ++ * by the error handler in scsi_restart_operations for new ++ * eh hosts. ++ * ++ * Note that we don't clear was_reset here; this is used by ++ * st.c, and either one or other has to die. + */ +- if (SDpnt->was_reset) { ++ if (SHpnt->hostt->use_new_eh_code == 0 && SDpnt->was_reset) { + /* + * We need to relock the door, but we might + * be in an interrupt handler. Only do this +@@ -918,7 +951,7 @@ + * this work. + */ + SDpnt->was_reset = 0; +- if (SDpnt->removable && !in_interrupt()) { ++ if (SDpnt->removable && SDpnt->locked && !in_interrupt()) { + spin_unlock_irq(&io_request_lock); + scsi_ioctl(SDpnt, SCSI_IOCTL_DOORLOCK, 0); + spin_lock_irq(&io_request_lock); +diff -urN linux-2.4.26/drivers/scsi/scsi_syms.c linux-2.4.26-vrs1/drivers/scsi/scsi_syms.c +--- linux-2.4.26/drivers/scsi/scsi_syms.c 2004-04-19 11:44:16.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/scsi/scsi_syms.c 2004-04-18 21:47:51.000000000 +0100 +@@ -104,3 +104,6 @@ + extern int scsi_delete_timer(Scsi_Cmnd *); + EXPORT_SYMBOL(scsi_add_timer); + EXPORT_SYMBOL(scsi_delete_timer); ++ ++extern int scsi_set_medium_removal(Scsi_Device *dev, char state); ++EXPORT_SYMBOL(scsi_set_medium_removal); +diff -urN linux-2.4.26/drivers/scsi/sd.c linux-2.4.26-vrs1/drivers/scsi/sd.c +--- linux-2.4.26/drivers/scsi/sd.c 2003-08-25 12:44:42.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/scsi/sd.c 2004-01-14 21:39:12.000000000 +0000 +@@ -399,6 +399,7 @@ + this_count = 0xffff; + + SCpnt->cmnd[0] += READ_10 - READ_6; ++ SCpnt->cmnd[1] |= 1 << 3; /* Set FUA --rmk */ + SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff; + SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff; + SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff; +@@ -524,7 +525,7 @@ + if (SDev->removable) + if (SDev->access_count==1) + if (scsi_block_when_processing_errors(SDev)) +- scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, NULL); ++ scsi_set_medium_removal(SDev, SCSI_REMOVAL_PREVENT); + + + return 0; +@@ -553,7 +554,7 @@ + if (SDev->removable) { + if (!SDev->access_count) + if (scsi_block_when_processing_errors(SDev)) +- scsi_ioctl(SDev, SCSI_IOCTL_DOORUNLOCK, NULL); ++ scsi_set_medium_removal(SDev, SCSI_REMOVAL_ALLOW); + } + if (SDev->host->hostt->module) + __MOD_DEC_USE_COUNT(SDev->host->hostt->module); +diff -urN linux-2.4.26/drivers/scsi/sr_ioctl.c linux-2.4.26-vrs1/drivers/scsi/sr_ioctl.c +--- linux-2.4.26/drivers/scsi/sr_ioctl.c 2002-11-28 23:53:14.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/scsi/sr_ioctl.c 2004-01-14 21:32:26.000000000 +0000 +@@ -214,9 +214,8 @@ + + int sr_lock_door(struct cdrom_device_info *cdi, int lock) + { +- return scsi_ioctl(scsi_CDs[MINOR(cdi->dev)].device, +- lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK, +- 0); ++ return scsi_set_medium_removal(scsi_CDs[MINOR(cdi->dev)].device, ++ lock ? SCSI_REMOVAL_PREVENT : SCSI_REMOVAL_ALLOW); + } + + int sr_drive_status(struct cdrom_device_info *cdi, int slot) +diff -urN linux-2.4.26/drivers/serial/21285.c linux-2.4.26-vrs1/drivers/serial/21285.c +--- linux-2.4.26/drivers/serial/21285.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/21285.c 2004-04-04 23:11:12.000000000 +0100 +@@ -0,0 +1,599 @@ ++/* ++ * linux/drivers/char/serial_21285.c ++ * ++ * Driver for the serial port on the 21285 StrongArm-110 core logic chip. ++ * ++ * Based on drivers/char/serial.c ++ * ++ * $Id: 21285.c,v 1.4.2.1 2002/10/24 09:53:23 rmk Exp $ ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define BAUD_BASE (mem_fclk_21285/64) ++ ++#ifdef CONFIG_DEVFS_FS ++#define SERIAL_21285_NAME "tts/FB%d" ++#define SERIAL_21285_AUXNAME "cua/FB%d" ++#else ++#define SERIAL_21285_NAME "ttyFB" ++#define SERIAL_21285_AUXNAME "cuafb" ++#endif ++ ++#define SERIAL_21285_MAJOR 204 ++#define SERIAL_21285_MINOR 4 ++ ++#define SERIAL_21285_AUXMAJOR 205 ++#define SERIAL_21285_AUXMINOR 4 ++ ++#ifdef CONFIG_SERIAL_21285_OLD ++#include ++/* ++ * Compatability with a mistake made a long time ago. ++ * Note - the use of "ttyI", "/dev/ttyS0" and major/minor 5,64 ++ * is HIGHLY DEPRECIATED, and will be removed in the 2.5 ++ * kernel series. ++ * -- rmk 15/04/2000 ++ */ ++#define SERIAL_21285_OLD_NAME "ttyI" ++#define SERIAL_21285_OLD_MAJOR TTY_MAJOR ++#define SERIAL_21285_OLD_MINOR 64 ++ ++static struct tty_driver rs285_old_driver; ++#endif ++ ++static struct tty_driver rs285_driver, callout_driver; ++static int rs285_refcount; ++static struct tty_struct *rs285_table[1]; ++ ++static struct termios *rs285_termios[1]; ++static struct termios *rs285_termios_locked[1]; ++ ++static char wbuf[1000], *putp = wbuf, *getp = wbuf, x_char; ++static struct tty_struct *rs285_tty; ++static DECLARE_MUTEX(rs285_sem); ++static int rs285_use_count; ++static unsigned long rs285_irq_enabled; ++ ++#define TX_IRQ_BIT (0) ++#define RX_IRQ_BIT (1) ++ ++static void rs285_stop_tx(void) ++{ ++ if (test_and_clear_bit(TX_IRQ_BIT, &rs285_irq_enabled)) ++ disable_irq(IRQ_CONTX); ++} ++ ++static void rs285_start_tx(void) ++{ ++ if (!test_and_set_bit(TX_IRQ_BIT, &rs285_irq_enabled)) ++ enable_irq(IRQ_CONTX); ++} ++ ++static void rs285_stop_rx(void) ++{ ++ if (test_and_clear_bit(RX_IRQ_BIT, &rs285_irq_enabled)) ++ disable_irq(IRQ_CONRX); ++} ++ ++static void rs285_start_rx(void) ++{ ++ if (!test_and_set_bit(RX_IRQ_BIT, &rs285_irq_enabled)) ++ enable_irq(IRQ_CONRX); ++} ++ ++static int rs285_write_room(struct tty_struct *tty) ++{ ++ return putp >= getp ? (sizeof(wbuf) - (long) putp + (long) getp) : ((long) getp - (long) putp - 1); ++} ++ ++static void rs285_rx_int(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ if (!rs285_tty) { ++ rs285_stop_rx(); ++ return; ++ } ++ while (!(*CSR_UARTFLG & 0x10)) { ++ int ch, flag; ++ ch = *CSR_UARTDR; ++ flag = *CSR_RXSTAT; ++ if (flag & 4) ++ tty_insert_flip_char(rs285_tty, 0, TTY_OVERRUN); ++ if (flag & 2) ++ flag = TTY_PARITY; ++ else if (flag & 1) ++ flag = TTY_FRAME; ++ tty_insert_flip_char(rs285_tty, ch, flag); ++ } ++ tty_flip_buffer_push(rs285_tty); ++} ++ ++static void rs285_send_xchar(struct tty_struct *tty, char ch) ++{ ++ x_char = ch; ++ rs285_start_tx(); ++} ++ ++static void rs285_throttle(struct tty_struct *tty) ++{ ++ if (I_IXOFF(tty)) ++ rs285_send_xchar(tty, STOP_CHAR(tty)); ++} ++ ++static void rs285_unthrottle(struct tty_struct *tty) ++{ ++ if (I_IXOFF(tty)) { ++ if (x_char) ++ x_char = 0; ++ else ++ rs285_send_xchar(tty, START_CHAR(tty)); ++ } ++} ++ ++static void rs285_tx_int(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ while (!(*CSR_UARTFLG & 0x20)) { ++ if (x_char) { ++ *CSR_UARTDR = x_char; ++ x_char = 0; ++ continue; ++ } ++ if (putp == getp) { ++ rs285_stop_tx(); ++ break; ++ } ++ *CSR_UARTDR = *getp; ++ if (++getp >= wbuf + sizeof(wbuf)) ++ getp = wbuf; ++ } ++ if (rs285_tty) ++ wake_up_interruptible(&rs285_tty->write_wait); ++} ++ ++static inline int rs285_xmit(int ch) ++{ ++ if (putp + 1 == getp || (putp + 1 == wbuf + sizeof(wbuf) && getp == wbuf)) ++ return 0; ++ *putp = ch; ++ if (++putp >= wbuf + sizeof(wbuf)) ++ putp = wbuf; ++ rs285_start_tx(); ++ return 1; ++} ++ ++static int rs285_write(struct tty_struct *tty, int from_user, ++ const u_char * buf, int count) ++{ ++ int i; ++ ++ if (from_user && verify_area(VERIFY_READ, buf, count)) ++ return -EINVAL; ++ ++ for (i = 0; i < count; i++) { ++ char ch; ++ if (from_user) ++ __get_user(ch, buf + i); ++ else ++ ch = buf[i]; ++ if (!rs285_xmit(ch)) ++ break; ++ } ++ return i; ++} ++ ++static void rs285_put_char(struct tty_struct *tty, u_char ch) ++{ ++ rs285_xmit(ch); ++} ++ ++static int rs285_chars_in_buffer(struct tty_struct *tty) ++{ ++ return sizeof(wbuf) - rs285_write_room(tty); ++} ++ ++static void rs285_flush_buffer(struct tty_struct *tty) ++{ ++ rs285_stop_tx(); ++ putp = getp = wbuf; ++ if (x_char) ++ rs285_start_tx(); ++} ++ ++static inline void rs285_set_cflag(int cflag) ++{ ++ int h_lcr, baud, quot; ++ ++ switch (cflag & CSIZE) { ++ case CS5: ++ h_lcr = 0x10; ++ break; ++ case CS6: ++ h_lcr = 0x30; ++ break; ++ case CS7: ++ h_lcr = 0x50; ++ break; ++ default: /* CS8 */ ++ h_lcr = 0x70; ++ break; ++ ++ } ++ if (cflag & CSTOPB) ++ h_lcr |= 0x08; ++ if (cflag & PARENB) ++ h_lcr |= 0x02; ++ if (!(cflag & PARODD)) ++ h_lcr |= 0x04; ++ ++ switch (cflag & CBAUD) { ++ case B200: baud = 200; break; ++ case B300: baud = 300; break; ++ case B1200: baud = 1200; break; ++ case B1800: baud = 1800; break; ++ case B2400: baud = 2400; break; ++ case B4800: baud = 4800; break; ++ default: ++ case B9600: baud = 9600; break; ++ case B19200: baud = 19200; break; ++ case B38400: baud = 38400; break; ++ case B57600: baud = 57600; break; ++ case B115200: baud = 115200; break; ++ } ++ ++ /* ++ * The documented expression for selecting the divisor is: ++ * BAUD_BASE / baud - 1 ++ * However, typically BAUD_BASE is not divisible by baud, so ++ * we want to select the divisor that gives us the minimum ++ * error. Therefore, we want: ++ * int(BAUD_BASE / baud - 0.5) -> ++ * int(BAUD_BASE / baud - (baud >> 1) / baud) -> ++ * int((BAUD_BASE - (baud >> 1)) / baud) ++ */ ++ quot = (BAUD_BASE - (baud >> 1)) / baud; ++ ++ *CSR_UARTCON = 0; ++ *CSR_L_UBRLCR = quot & 0xff; ++ *CSR_M_UBRLCR = (quot >> 8) & 0x0f; ++ *CSR_H_UBRLCR = h_lcr; ++ *CSR_UARTCON = 1; ++} ++ ++static void rs285_set_termios(struct tty_struct *tty, struct termios *old) ++{ ++ if (old && tty->termios->c_cflag == old->c_cflag) ++ return; ++ rs285_set_cflag(tty->termios->c_cflag); ++} ++ ++ ++static void rs285_stop(struct tty_struct *tty) ++{ ++ rs285_stop_tx(); ++} ++ ++static void rs285_start(struct tty_struct *tty) ++{ ++ rs285_start_tx(); ++} ++ ++static void rs285_wait_until_sent(struct tty_struct *tty, int timeout) ++{ ++ int orig_jiffies = jiffies; ++ while (*CSR_UARTFLG & 8) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ if (signal_pending(current)) ++ break; ++ if (timeout && time_after(jiffies, orig_jiffies + timeout)) ++ break; ++ } ++ set_current_state(TASK_RUNNING); ++} ++ ++static int rs285_open(struct tty_struct *tty, struct file *filp) ++{ ++ int line, ret; ++ ++ MOD_INC_USE_COUNT; ++ ++ line = MINOR(tty->device) - tty->driver.minor_start; ++ if (line) ++ return -ENODEV; ++ ++ ret = down_interruptible(&rs285_sem); ++ if (ret) ++ return ret; ++ ++ tty->driver_data = NULL; ++ rs285_tty = tty; ++ ++ if (rs285_use_count == 0) { ++ rs285_irq_enabled = 3; ++ ret = request_irq(IRQ_CONRX, rs285_rx_int, 0, "rs285", NULL); ++ if (ret == 0) { ++ ret = request_irq(IRQ_CONTX, rs285_tx_int, 0, "rs285", ++ NULL); ++ if (ret) ++ free_irq(IRQ_CONRX, NULL); ++ } ++ } ++ ++ if (ret == 0) ++ rs285_use_count++; ++ ++ up(&rs285_sem); ++ ++ return ret; ++} ++ ++static void rs285_close(struct tty_struct *tty, struct file *filp) ++{ ++ down(&rs285_sem); ++ if (!--rs285_use_count) { ++ rs285_wait_until_sent(tty, 0); ++ rs285_stop_rx(); ++ rs285_stop_tx(); ++ rs285_tty = NULL; ++ free_irq(IRQ_CONTX, NULL); ++ free_irq(IRQ_CONRX, NULL); ++ } ++ up(&rs285_sem); ++ MOD_DEC_USE_COUNT; ++} ++ ++static int __init rs285_init(void) ++{ ++ int baud = B9600; ++ ++ if (machine_is_personal_server()) ++ baud = B57600; ++ ++ rs285_driver.magic = TTY_DRIVER_MAGIC; ++ rs285_driver.driver_name = "serial_21285"; ++ rs285_driver.name = SERIAL_21285_NAME; ++ rs285_driver.major = SERIAL_21285_MAJOR; ++ rs285_driver.minor_start = SERIAL_21285_MINOR; ++ rs285_driver.num = 1; ++ rs285_driver.type = TTY_DRIVER_TYPE_SERIAL; ++ rs285_driver.subtype = SERIAL_TYPE_NORMAL; ++ rs285_driver.init_termios = tty_std_termios; ++ rs285_driver.init_termios.c_cflag = baud | CS8 | CREAD | HUPCL | CLOCAL; ++ rs285_driver.flags = TTY_DRIVER_REAL_RAW; ++ rs285_driver.refcount = &rs285_refcount; ++ rs285_driver.table = rs285_table; ++ rs285_driver.termios = rs285_termios; ++ rs285_driver.termios_locked = rs285_termios_locked; ++ ++ rs285_driver.open = rs285_open; ++ rs285_driver.close = rs285_close; ++ rs285_driver.write = rs285_write; ++ rs285_driver.put_char = rs285_put_char; ++ rs285_driver.write_room = rs285_write_room; ++ rs285_driver.chars_in_buffer = rs285_chars_in_buffer; ++ rs285_driver.flush_buffer = rs285_flush_buffer; ++ rs285_driver.throttle = rs285_throttle; ++ rs285_driver.unthrottle = rs285_unthrottle; ++ rs285_driver.send_xchar = rs285_send_xchar; ++ rs285_driver.set_termios = rs285_set_termios; ++ rs285_driver.stop = rs285_stop; ++ rs285_driver.start = rs285_start; ++ rs285_driver.wait_until_sent = rs285_wait_until_sent; ++ ++ callout_driver = rs285_driver; ++ callout_driver.name = SERIAL_21285_AUXNAME; ++ callout_driver.major = SERIAL_21285_AUXMAJOR; ++ callout_driver.subtype = SERIAL_TYPE_CALLOUT; ++ ++#ifdef CONFIG_SERIAL_21285_OLD ++ if (!machine_is_ebsa285() && !machine_is_netwinder()) { ++ rs285_old_driver = rs285_driver; ++ rs285_old_driver.name = SERIAL_21285_OLD_NAME; ++ rs285_old_driver.major = SERIAL_21285_OLD_MAJOR; ++ rs285_old_driver.minor_start = SERIAL_21285_OLD_MINOR; ++ ++ if (tty_register_driver(&rs285_old_driver)) ++ printk(KERN_ERR "Couldn't register old 21285 serial driver\n"); ++ } ++#endif ++ ++ if (tty_register_driver(&rs285_driver)) ++ printk(KERN_ERR "Couldn't register 21285 serial driver\n"); ++ if (tty_register_driver(&callout_driver)) ++ printk(KERN_ERR "Couldn't register 21285 callout driver\n"); ++ ++ return 0; ++} ++ ++static void __exit rs285_fini(void) ++{ ++ unsigned long flags; ++ int ret; ++ ++ save_flags(flags); ++ cli(); ++ ret = tty_unregister_driver(&callout_driver); ++ if (ret) ++ printk(KERN_ERR "Unable to unregister 21285 callout driver " ++ "(%d)\n", ret); ++ ret = tty_unregister_driver(&rs285_driver); ++ if (ret) ++ printk(KERN_ERR "Unable to unregister 21285 driver (%d)\n", ++ ret); ++#ifdef CONFIG_SERIAL_21285_OLD ++ if (!machine_is_ebsa285() && !machine_is_netwinder()) { ++ ret = tty_unregister_driver(&rs285_old_driver); ++ if (ret) ++ printk(KERN_ERR "Unable to unregister old 21285 " ++ "driver (%d)\n", ret); ++ } ++#endif ++ free_irq(IRQ_CONTX, NULL); ++ free_irq(IRQ_CONRX, NULL); ++ restore_flags(flags); ++} ++ ++module_init(rs285_init); ++module_exit(rs285_fini); ++ ++#ifdef CONFIG_SERIAL_21285_CONSOLE ++/************** console driver *****************/ ++ ++static void rs285_console_write(struct console *co, const char *s, u_int count) ++{ ++ int i; ++ ++ rs285_stop_tx(); ++ for (i = 0; i < count; i++) { ++ while (*CSR_UARTFLG & 0x20); ++ *CSR_UARTDR = s[i]; ++ if (s[i] == '\n') { ++ while (*CSR_UARTFLG & 0x20); ++ *CSR_UARTDR = '\r'; ++ } ++ } ++ rs285_start_tx(); ++} ++ ++static kdev_t rs285_console_device(struct console *c) ++{ ++ return MKDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR); ++} ++ ++static int __init rs285_console_setup(struct console *co, char *options) ++{ ++ int baud = 9600; ++ int bits = 8; ++ int parity = 'n'; ++ int flow; ++ int cflag = CREAD | HUPCL | CLOCAL; ++ ++ if (machine_is_personal_server()) ++ baud = 57600; ++ ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ ++ /* ++ * Now construct a cflag setting. ++ */ ++ switch (baud) { ++ case 1200: ++ cflag |= B1200; ++ break; ++ case 2400: ++ cflag |= B2400; ++ break; ++ case 4800: ++ cflag |= B4800; ++ break; ++ case 9600: ++ cflag |= B9600; ++ break; ++ case 19200: ++ cflag |= B19200; ++ break; ++ case 38400: ++ cflag |= B38400; ++ break; ++ case 57600: ++ cflag |= B57600; ++ break; ++ case 115200: ++ cflag |= B115200; ++ break; ++ default: ++ cflag |= B9600; ++ break; ++ } ++ switch (bits) { ++ case 7: ++ cflag |= CS7; ++ break; ++ default: ++ cflag |= CS8; ++ break; ++ } ++ switch (parity) { ++ case 'o': ++ case 'O': ++ cflag |= PARODD; ++ break; ++ case 'e': ++ case 'E': ++ cflag |= PARENB; ++ break; ++ } ++ co->cflag = cflag; ++ rs285_set_cflag(cflag); ++ rs285_console_write(NULL, "\e[2J\e[Hboot ", 12); ++ if (options) ++ rs285_console_write(NULL, options, strlen(options)); ++ else ++ rs285_console_write(NULL, "no options", 10); ++ rs285_console_write(NULL, "\n", 1); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_SERIAL_21285_OLD ++static struct console rs285_old_cons = ++{ ++ SERIAL_21285_OLD_NAME, ++ rs285_console_write, ++ NULL, ++ rs285_console_device, ++ NULL, ++ rs285_console_setup, ++ CON_PRINTBUFFER, ++ -1, ++ 0, ++ NULL ++}; ++#endif ++ ++static struct console rs285_cons = ++{ ++ name: SERIAL_21285_NAME, ++ write: rs285_console_write, ++ device: rs285_console_device, ++ setup: rs285_console_setup, ++ flags: CON_PRINTBUFFER, ++ index: -1, ++}; ++ ++void __init rs285_console_init(void) ++{ ++#ifdef CONFIG_SERIAL_21285_OLD ++ if (!machine_is_ebsa285() && !machine_is_netwinder()) ++ register_console(&rs285_old_cons); ++#endif ++ register_console(&rs285_cons); ++} ++ ++#endif /* CONFIG_SERIAL_21285_CONSOLE */ ++ ++EXPORT_NO_SYMBOLS; ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver"); +diff -urN linux-2.4.26/drivers/serial/8250.c linux-2.4.26-vrs1/drivers/serial/8250.c +--- linux-2.4.26/drivers/serial/8250.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/8250.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,2170 @@ ++/* ++ * linux/drivers/serial/8250.c ++ * ++ * Driver for 8250/16550-type serial ports ++ * ++ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. ++ * ++ * Copyright (C) 2001 Russell King. ++ * ++ * 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. ++ * ++ * $Id: 8250.c,v 1.14.2.8 2002/10/24 14:31:31 rmk Exp $ ++ * ++ * A note about mapbase / membase ++ * ++ * mapbase is the physical address of the IO port. Currently, we don't ++ * support this very well, and it may well be dropped from this driver ++ * in future. As such, mapbase should be NULL. ++ * ++ * membase is an 'ioremapped' cookie. This is compatible with the old ++ * serial.c driver, and is currently the preferred form. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "8250.h" ++ ++/* ++ * Configuration: ++ * share_irqs - whether we pass SA_SHIRQ to request_irq(). This option ++ * is unsafe when used on edge-triggered interrupts. ++ */ ++unsigned int share_irqs = SERIAL8250_SHARE_IRQS; ++ ++/* ++ * Debugging. ++ */ ++#if 0 ++#define DEBUG_AUTOCONF(fmt...) printk(fmt) ++#else ++#define DEBUG_AUTOCONF(fmt...) do { } while (0) ++#endif ++ ++#if 0 ++#define DEBUG_INTR(fmt...) printk(fmt) ++#else ++#define DEBUG_INTR(fmt...) do { } while (0) ++#endif ++ ++#define PASS_LIMIT 256 ++ ++/* ++ * We default to IRQ0 for the "no irq" hack. Some ++ * machine types want others as well - they're free ++ * to redefine this in their header file. ++ */ ++#define is_real_interrupt(irq) ((irq) != 0) ++ ++/* ++ * This converts from our new CONFIG_ symbols to the symbols ++ * that asm/serial.h expects. You _NEED_ to comment out the ++ * linux/config.h include contained inside asm/serial.h for ++ * this to work. ++ */ ++#undef CONFIG_SERIAL_MANY_PORTS ++#undef CONFIG_SERIAL_DETECT_IRQ ++#undef CONFIG_SERIAL_MULTIPORT ++#undef CONFIG_HUB6 ++ ++#ifdef CONFIG_SERIAL_8250_DETECT_IRQ ++#define CONFIG_SERIAL_DETECT_IRQ 1 ++#endif ++#ifdef CONFIG_SERIAL_8250_MULTIPORT ++#define CONFIG_SERIAL_MULTIPORT 1 ++#endif ++#ifdef CONFIG_SERIAL_8250_HUB6 ++#define CONFIG_HUB6 1 ++#endif ++#ifdef CONFIG_SERIAL_8250_MANY_PORTS ++#define CONFIG_SERIAL_MANY_PORTS 1 ++#endif ++ ++#include ++ ++static struct old_serial_port old_serial_port[] = { ++ SERIAL_PORT_DFNS /* defined in asm/serial.h */ ++}; ++ ++#define UART_NR ARRAY_SIZE(old_serial_port) ++ ++static struct tty_driver normal, callout; ++static struct tty_struct *serial8250_table[UART_NR]; ++static struct termios *serial8250_termios[UART_NR], *serial8250_termios_locked[UART_NR]; ++ ++#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE) ++ ++#define PORT_RSA_MAX 4 ++static int probe_rsa[PORT_RSA_MAX]; ++static int force_rsa[PORT_RSA_MAX]; ++#endif /* CONFIG_SERIAL_8250_RSA */ ++ ++struct uart_8250_port { ++ struct uart_port port; ++ struct timer_list timer; /* "no irq" timer */ ++ struct list_head list; /* ports on this IRQ */ ++ unsigned int capabilities; /* port capabilities */ ++ unsigned char acr; ++ unsigned char ier; ++ unsigned short rev; ++ unsigned char lcr; ++ unsigned char mcr; ++ unsigned char mcr_mask; /* mask of user bits */ ++ unsigned char mcr_force; /* mask of forced bits */ ++ unsigned char efr; ++ unsigned int lsr_break_flag; ++ ++ /* ++ * We provide a per-port pm hook. ++ */ ++ void (*pm)(struct uart_port *port, ++ unsigned int state, unsigned int old); ++}; ++ ++struct irq_info { ++ spinlock_t lock; ++ struct list_head *head; ++}; ++ ++static struct irq_info irq_lists[NR_IRQS]; ++ ++/* ++ * Here we define the default xmit fifo size used for each type of UART. ++ */ ++static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = { ++ { "unknown", 1, 0 }, ++ { "8250", 1, 0 }, ++ { "16450", 1, 0 }, ++ { "16550", 1, 0 }, ++ { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, ++ { "Cirrus", 1, 0 }, ++ { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, ++ { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, ++ { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO }, ++ { "Startech", 1, 0 }, ++ { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO }, ++ { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, ++ { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, ++ { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO } ++}; ++ ++static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset) ++{ ++ offset <<= up->port.regshift; ++ ++ switch (up->port.iotype) { ++#ifdef CONFIG_SERIAL_8250_HUB6 ++ case SERIAL_IO_HUB6: ++ outb(up->port.hub6 - 1 + offset, up->port.iobase); ++ return inb(up->port.iobase + 1); ++#endif ++ ++ case SERIAL_IO_MEM: ++ return readb(up->port.membase + offset); ++ ++ default: ++ return inb(up->port.iobase + offset); ++ } ++} ++ ++static _INLINE_ void ++serial_out(struct uart_8250_port *up, int offset, int value) ++{ ++ offset <<= up->port.regshift; ++ ++ switch (up->port.iotype) { ++#ifdef CONFIG_SERIAL_8250_HUB6 ++ case SERIAL_IO_HUB6: ++ outb(up->port.hub6 - 1 + offset, up->port.iobase); ++ outb(value, up->port.iobase + 1); ++ break; ++#endif ++ ++ case SERIAL_IO_MEM: ++ writeb(value, up->port.membase + offset); ++ break; ++ ++ default: ++ outb(value, up->port.iobase + offset); ++ } ++} ++ ++/* ++ * We used to support using pause I/O for certain machines. We ++ * haven't supported this for a while, but just in case it's badly ++ * needed for certain old 386 machines, I've left these #define's ++ * in.... ++ */ ++#define serial_inp(up, offset) serial_in(up, offset) ++#define serial_outp(up, offset, value) serial_out(up, offset, value) ++ ++ ++/* ++ * For the 16C950 ++ */ ++static void serial_icr_write(struct uart_8250_port *up, int offset, int value) ++{ ++ serial_out(up, UART_SCR, offset); ++ serial_out(up, UART_ICR, value); ++} ++ ++static unsigned int serial_icr_read(struct uart_8250_port *up, int offset) ++{ ++ unsigned int value; ++ ++ serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); ++ serial_out(up, UART_SCR, offset); ++ value = serial_in(up, UART_ICR); ++ serial_icr_write(up, UART_ACR, up->acr); ++ ++ return value; ++} ++ ++#ifdef CONFIG_SERIAL_8250_RSA ++/* ++ * Attempts to turn on the RSA FIFO. Returns zero on failure. ++ * We set the port uart clock rate if we succeed. ++ */ ++static int __enable_rsa(struct uart_8250_port *up) ++{ ++ unsigned char mode; ++ int result; ++ ++ mode = serial_inp(up, UART_RSA_MSR); ++ result = mode & UART_RSA_MSR_FIFO; ++ ++ if (!result) { ++ serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); ++ mode = serial_inp(up, UART_RSA_MSR); ++ result = mode & UART_RSA_MSR_FIFO; ++ } ++ ++ if (result) ++ up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16; ++ ++ return result; ++} ++ ++static void enable_rsa(struct uart_8250_port *up) ++{ ++ if (up->port.type == PORT_RSA) { ++ if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { ++ spin_lock_irq(&up->port.lock); ++ __enable_rsa(up); ++ spin_unlock_irq(&up->port.lock); ++ } ++ if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) ++ serial_outp(up, UART_RSA_FRR, 0); ++ } ++} ++ ++/* ++ * Attempts to turn off the RSA FIFO. Returns zero on failure. ++ * It is unknown why interrupts were disabled in here. However, ++ * the caller is expected to preserve this behaviour by grabbing ++ * the spinlock before calling this function. ++ */ ++static void disable_rsa(struct uart_8250_port *up) ++{ ++ unsigned char mode; ++ int result; ++ ++ if (up->port.type == PORT_RSA && ++ up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { ++ spin_lock_irq(&up->port.lock); ++ ++ mode = serial_inp(up, UART_RSA_MSR); ++ result = !(mode & UART_RSA_MSR_FIFO); ++ ++ if (!result) { ++ serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); ++ mode = serial_inp(up, UART_RSA_MSR); ++ result = !(mode & UART_RSA_MSR_FIFO); ++ } ++ ++ if (result) ++ up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; ++ spin_unlock_irq(&up->port.lock); ++ } ++} ++#endif /* CONFIG_SERIAL_8250_RSA */ ++ ++/* ++ * This is a quickie test to see how big the FIFO is. ++ * It doesn't work at all the time, more's the pity. ++ */ ++static int size_fifo(struct uart_8250_port *up) ++{ ++ unsigned char old_fcr, old_mcr, old_dll, old_dlm; ++ int count; ++ ++ old_fcr = serial_inp(up, UART_FCR); ++ old_mcr = serial_inp(up, UART_MCR); ++ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | ++ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); ++ serial_outp(up, UART_MCR, UART_MCR_LOOP); ++ serial_outp(up, UART_LCR, UART_LCR_DLAB); ++ old_dll = serial_inp(up, UART_DLL); ++ old_dlm = serial_inp(up, UART_DLM); ++ serial_outp(up, UART_DLL, 0x01); ++ serial_outp(up, UART_DLM, 0x00); ++ serial_outp(up, UART_LCR, 0x03); ++ for (count = 0; count < 256; count++) ++ serial_outp(up, UART_TX, count); ++ mdelay(20);/* FIXME - schedule_timeout */ ++ for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) && ++ (count < 256); count++) ++ serial_inp(up, UART_RX); ++ serial_outp(up, UART_FCR, old_fcr); ++ serial_outp(up, UART_MCR, old_mcr); ++ serial_outp(up, UART_LCR, UART_LCR_DLAB); ++ serial_outp(up, UART_DLL, old_dll); ++ serial_outp(up, UART_DLM, old_dlm); ++ ++ return count; ++} ++ ++/* ++ * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. ++ * When this function is called we know it is at least a StarTech ++ * 16650 V2, but it might be one of several StarTech UARTs, or one of ++ * its clones. (We treat the broken original StarTech 16650 V1 as a ++ * 16550, and why not? Startech doesn't seem to even acknowledge its ++ * existence.) ++ * ++ * What evil have men's minds wrought... ++ */ ++static void autoconfig_has_efr(struct uart_8250_port *up) ++{ ++ unsigned char id1, id2, id3, rev, saved_dll, saved_dlm; ++ ++ /* ++ * First we check to see if it's an Oxford Semiconductor UART. ++ * ++ * If we have to do this here because some non-National ++ * Semiconductor clone chips lock up if you try writing to the ++ * LSR register (which serial_icr_read does) ++ */ ++ ++ /* ++ * Check for Oxford Semiconductor 16C950. ++ * ++ * EFR [4] must be set else this test fails. ++ * ++ * This shouldn't be necessary, but Mike Hudson (Exoray@isys.ca) ++ * claims that it's needed for 952 dual UART's (which are not ++ * recommended for new designs). ++ */ ++ up->acr = 0; ++ serial_out(up, UART_LCR, 0xBF); ++ serial_out(up, UART_EFR, 0x10); ++ serial_out(up, UART_LCR, 0x00); ++ id1 = serial_icr_read(up, UART_ID1); ++ id2 = serial_icr_read(up, UART_ID2); ++ id3 = serial_icr_read(up, UART_ID3); ++ rev = serial_icr_read(up, UART_REV); ++ ++ DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev); ++ ++ if (id1 == 0x16 && id2 == 0xC9 && ++ (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) { ++ up->port.type = PORT_16C950; ++ up->rev = rev | (id3 << 8); ++ return; ++ } ++ ++ /* ++ * We check for a XR16C850 by setting DLL and DLM to 0, and then ++ * reading back DLL and DLM. The chip type depends on the DLM ++ * value read back: ++ * 0x10 - XR16C850 and the DLL contains the chip revision. ++ * 0x12 - XR16C2850. ++ * 0x14 - XR16C854. ++ */ ++ serial_outp(up, UART_LCR, UART_LCR_DLAB); ++ saved_dll = serial_inp(up, UART_DLL); ++ saved_dlm = serial_inp(up, UART_DLM); ++ serial_outp(up, UART_DLL, 0); ++ serial_outp(up, UART_DLM, 0); ++ id2 = serial_inp(up, UART_DLL); ++ id1 = serial_inp(up, UART_DLM); ++ serial_outp(up, UART_DLL, saved_dll); ++ serial_outp(up, UART_DLM, saved_dlm); ++ ++ DEBUG_AUTOCONF("850id=%02x:%02x ", id1, id2); ++ ++ if (id1 == 0x10 || id1 == 0x12 || id1 == 0x14) { ++ if (id1 == 0x10) ++ up->rev = id2; ++ up->port.type = PORT_16850; ++ return; ++ } ++ ++ /* ++ * It wasn't an XR16C850. ++ * ++ * We distinguish between the '654 and the '650 by counting ++ * how many bytes are in the FIFO. I'm using this for now, ++ * since that's the technique that was sent to me in the ++ * serial driver update, but I'm not convinced this works. ++ * I've had problems doing this in the past. -TYT ++ */ ++ if (size_fifo(up) == 64) ++ up->port.type = PORT_16654; ++ else ++ up->port.type = PORT_16650V2; ++} ++ ++/* ++ * We detected a chip without a FIFO. Only two fall into ++ * this category - the original 8250 and the 16450. The ++ * 16450 has a scratch register (accessible with LCR=0) ++ */ ++static void autoconfig_8250(struct uart_8250_port *up) ++{ ++ unsigned char scratch, status1, status2; ++ ++ up->port.type = PORT_8250; ++ ++ scratch = serial_in(up, UART_SCR); ++ serial_outp(up, UART_SCR, 0xa5); ++ status1 = serial_in(up, UART_SCR); ++ serial_outp(up, UART_SCR, 0x5a); ++ status2 = serial_in(up, UART_SCR); ++ serial_outp(up, UART_SCR, scratch); ++ ++ if (status1 == 0xa5 && status2 == 0x5a) ++ up->port.type = PORT_16450; ++} ++ ++/* ++ * We know that the chip has FIFOs. Does it have an EFR? The ++ * EFR is located in the same register position as the IIR and ++ * we know the top two bits of the IIR are currently set. The ++ * EFR should contain zero. Try to read the EFR. ++ */ ++static void autoconfig_16550a(struct uart_8250_port *up) ++{ ++ unsigned char status1, status2; ++ ++ up->port.type = PORT_16550A; ++ ++ /* ++ * Check for presence of the EFR when DLAB is set. ++ * Only ST16C650V1 UARTs pass this test. ++ */ ++ serial_outp(up, UART_LCR, UART_LCR_DLAB); ++ if (serial_in(up, UART_EFR) == 0) { ++ DEBUG_AUTOCONF("EFRv1 "); ++ up->port.type = PORT_16650; ++ return; ++ } ++ ++ /* ++ * Maybe it requires 0xbf to be written to the LCR. ++ * (other ST16C650V2 UARTs, TI16C752A, etc) ++ */ ++ serial_outp(up, UART_LCR, 0xBF); ++ if (serial_in(up, UART_EFR) == 0) { ++ DEBUG_AUTOCONF("EFRv2 "); ++ autoconfig_has_efr(up); ++ return; ++ } ++ ++ /* ++ * Check for a National Semiconductor SuperIO chip. ++ * Attempt to switch to bank 2, read the value of the LOOP bit ++ * from EXCR1. Switch back to bank 0, change it in MCR. Then ++ * switch back to bank 2, read it from EXCR1 again and check ++ * it's changed. If so, set baud_base in EXCR2 to 921600. ++ */ ++ serial_outp(up, UART_LCR, 0); ++ status1 = serial_in(up, UART_MCR); ++ serial_outp(up, UART_LCR, 0xE0); ++ status2 = serial_in(up, 0x02); /* EXCR1 */ ++ ++ if (!((status2 ^ status1) & UART_MCR_LOOP)) { ++ serial_outp(up, UART_LCR, 0); ++ serial_outp(up, UART_MCR, status1 ^ UART_MCR_LOOP); ++ serial_outp(up, UART_LCR, 0xE0); ++ status2 = serial_in(up, 0x02); /* EXCR1 */ ++ serial_outp(up, UART_LCR, 0); ++ serial_outp(up, UART_MCR, status1); ++ ++ if ((status2 ^ status1) & UART_MCR_LOOP) { ++ serial_outp(up, UART_LCR, 0xE0); ++ status1 = serial_in(up, 0x04); /* EXCR1 */ ++ status1 &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ ++ status1 |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ ++ serial_outp(up, 0x04, status1); ++ serial_outp(up, UART_LCR, 0); ++ ++ up->port.type = PORT_NS16550A; ++ up->port.uartclk = 921600*16; ++ return; ++ } ++ } ++ ++ /* ++ * No EFR. Try to detect a TI16750, which only sets bit 5 of ++ * the IIR when 64 byte FIFO mode is enabled when DLAB is set. ++ * Try setting it with and without DLAB set. Cheap clones ++ * set bit 5 without DLAB set. ++ */ ++ serial_outp(up, UART_LCR, 0); ++ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); ++ status1 = serial_in(up, UART_IIR) >> 5; ++ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); ++ serial_outp(up, UART_LCR, UART_LCR_DLAB); ++ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); ++ status2 = serial_in(up, UART_IIR) >> 5; ++ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); ++ ++ DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2); ++ ++ if (status1 == 6 && status2 == 7) { ++ up->port.type = PORT_16750; ++ return; ++ } ++} ++ ++/* ++ * This routine is called by rs_init() to initialize a specific serial ++ * port. It determines what type of UART chip this serial port is ++ * using: 8250, 16450, 16550, 16550A. The important question is ++ * whether or not this UART is a 16550A or not, since this will ++ * determine whether or not we can use its FIFO features or not. ++ */ ++static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) ++{ ++ unsigned char status1, scratch, scratch2, scratch3; ++ unsigned char save_lcr, save_mcr; ++ unsigned long flags; ++ ++ if (!up->port.iobase && !up->port.mapbase && !up->port.membase) ++ return; ++ ++ DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%p): ", ++ up->port.line, up->port.iobase, up->port.membase); ++ ++ /* ++ * We really do need global IRQs disabled here - we're going to ++ * be frobbing the chips IRQ enable register to see if it exists. ++ */ ++ spin_lock_irqsave(&up->port.lock, flags); ++ ++ if (!(up->port.flags & UPF_BUGGY_UART)) { ++ /* ++ * Do a simple existence test first; if we fail this, ++ * there's no point trying anything else. ++ * ++ * 0x80 is used as a nonsense port to prevent against ++ * false positives due to ISA bus float. The ++ * assumption is that 0x80 is a non-existent port; ++ * which should be safe since include/asm/io.h also ++ * makes this assumption. ++ * ++ * Note: this is safe as long as MCR bit 4 is clear ++ * and the device is in "PC" mode. ++ */ ++ scratch = serial_inp(up, UART_IER); ++ serial_outp(up, UART_IER, 0); ++#ifdef __i386__ ++ outb(0xff, 0x080); ++#endif ++ scratch2 = serial_inp(up, UART_IER); ++ serial_outp(up, UART_IER, 0x0F); ++#ifdef __i386__ ++ outb(0, 0x080); ++#endif ++ scratch3 = serial_inp(up, UART_IER); ++ serial_outp(up, UART_IER, scratch); ++ if (scratch2 != 0 || scratch3 != 0x0F) { ++ /* ++ * We failed; there's nothing here ++ */ ++ DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", ++ scratch2, scratch3); ++ goto out; ++ } ++ } ++ ++ save_mcr = serial_in(up, UART_MCR); ++ save_lcr = serial_in(up, UART_LCR); ++ ++ /* ++ * Check to see if a UART is really there. Certain broken ++ * internal modems based on the Rockwell chipset fail this ++ * test, because they apparently don't implement the loopback ++ * test mode. So this test is skipped on the COM 1 through ++ * COM 4 ports. This *should* be safe, since no board ++ * manufacturer would be stupid enough to design a board ++ * that conflicts with COM 1-4 --- we hope! ++ */ ++ if (!(up->port.flags & UPF_SKIP_TEST)) { ++ serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); ++ status1 = serial_inp(up, UART_MSR) & 0xF0; ++ serial_outp(up, UART_MCR, save_mcr); ++ if (status1 != 0x90) { ++ DEBUG_AUTOCONF("LOOP test failed (%02x) ", ++ status1); ++ goto out; ++ } ++ } ++ ++ /* ++ * We're pretty sure there's a port here. Lets find out what ++ * type of port it is. The IIR top two bits allows us to find ++ * out if its 8250 or 16450, 16550, 16550A or later. This ++ * determines what we test for next. ++ * ++ * We also initialise the EFR (if any) to zero for later. The ++ * EFR occupies the same register location as the FCR and IIR. ++ */ ++ serial_outp(up, UART_LCR, 0xBF); ++ serial_outp(up, UART_EFR, 0); ++ serial_outp(up, UART_LCR, 0); ++ ++ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); ++ scratch = serial_in(up, UART_IIR) >> 6; ++ ++ DEBUG_AUTOCONF("iir=%d ", scratch); ++ ++ switch (scratch) { ++ case 0: ++ autoconfig_8250(up); ++ break; ++ case 1: ++ up->port.type = PORT_UNKNOWN; ++ break; ++ case 2: ++ up->port.type = PORT_16550; ++ break; ++ case 3: ++ autoconfig_16550a(up); ++ break; ++ } ++ ++#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE) ++ /* ++ * Only probe for RSA ports if we got the region. ++ */ ++ if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) { ++ int i; ++ ++ for (i = 0 ; i < PORT_RSA_MAX ; ++i) { ++ if (!probe_rsa[i] && !force_rsa[i]) ++ break; ++ if (((probe_rsa[i] != up->port.iobase) || ++ check_region(up->port.iobase + UART_RSA_BASE, 16)) && ++ (force_rsa[i] != up->port.iobase)) ++ continue; ++ if (__enable_rsa(up)) { ++ up->port.type = PORT_RSA; ++ break; ++ } ++ } ++ } ++#endif ++ serial_outp(up, UART_LCR, save_lcr); ++ ++ up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; ++ up->capabilities = uart_config[up->port.type].flags; ++ ++ if (up->port.type == PORT_UNKNOWN) ++ goto out; ++ ++ /* ++ * Reset the UART. ++ */ ++#ifdef CONFIG_SERIAL_8250_RSA ++ if (up->port.type == PORT_RSA) ++ serial_outp(up, UART_RSA_FRR, 0); ++#endif ++ serial_outp(up, UART_MCR, save_mcr); ++ serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO | ++ UART_FCR_CLEAR_RCVR | ++ UART_FCR_CLEAR_XMIT)); ++ serial_outp(up, UART_FCR, 0); ++ (void)serial_in(up, UART_RX); ++ serial_outp(up, UART_IER, 0); ++ ++ out: ++ spin_unlock_irqrestore(&up->port.lock, flags); ++ ++#ifdef CONFIG_SERIAL_8250_RSA ++ if (up->port.iobase && up->port.type == PORT_RSA) { ++ release_region(up->port.iobase, 8); ++ request_region(up->port.iobase + UART_RSA_BASE, 16, ++ "serial_rsa"); ++ } ++#endif ++ DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name); ++} ++ ++static void autoconfig_irq(struct uart_8250_port *up) ++{ ++ unsigned char save_mcr, save_ier; ++ unsigned char save_ICP = 0; ++ unsigned int ICP = 0; ++ unsigned long irqs; ++ int irq; ++ ++ if (up->port.flags & UPF_FOURPORT) { ++ ICP = (up->port.iobase & 0xfe0) | 0x1f; ++ save_ICP = inb_p(ICP); ++ outb_p(0x80, ICP); ++ (void) inb_p(ICP); ++ } ++ ++ /* forget possible initially masked and pending IRQ */ ++ probe_irq_off(probe_irq_on()); ++ save_mcr = serial_inp(up, UART_MCR); ++ save_ier = serial_inp(up, UART_IER); ++ serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); ++ ++ irqs = probe_irq_on(); ++ serial_outp(up, UART_MCR, 0); ++ udelay (10); ++ if (up->port.flags & UPF_FOURPORT) { ++ serial_outp(up, UART_MCR, ++ UART_MCR_DTR | UART_MCR_RTS); ++ } else { ++ serial_outp(up, UART_MCR, ++ UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); ++ } ++ serial_outp(up, UART_IER, 0x0f); /* enable all intrs */ ++ (void)serial_inp(up, UART_LSR); ++ (void)serial_inp(up, UART_RX); ++ (void)serial_inp(up, UART_IIR); ++ (void)serial_inp(up, UART_MSR); ++ serial_outp(up, UART_TX, 0xFF); ++ udelay (20); ++ irq = probe_irq_off(irqs); ++ ++ serial_outp(up, UART_MCR, save_mcr); ++ serial_outp(up, UART_IER, save_ier); ++ ++ if (up->port.flags & UPF_FOURPORT) ++ outb_p(save_ICP, ICP); ++ ++ up->port.irq = (irq > 0) ? irq : 0; ++} ++ ++static void serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ ++ if (up->ier & UART_IER_THRI) { ++ up->ier &= ~UART_IER_THRI; ++ serial_out(up, UART_IER, up->ier); ++ } ++ if (up->port.type == PORT_16C950 && tty_stop) { ++ up->acr |= UART_ACR_TXDIS; ++ serial_icr_write(up, UART_ACR, up->acr); ++ } ++} ++ ++static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ ++ if (!(up->ier & UART_IER_THRI)) { ++ up->ier |= UART_IER_THRI; ++ serial_out(up, UART_IER, up->ier); ++ } ++ /* ++ * We only do this from uart_start ++ */ ++ if (tty_start && up->port.type == PORT_16C950) { ++ up->acr &= ~UART_ACR_TXDIS; ++ serial_icr_write(up, UART_ACR, up->acr); ++ } ++} ++ ++static void serial8250_stop_rx(struct uart_port *port) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ ++ up->ier &= ~UART_IER_RLSI; ++ up->port.read_status_mask &= ~UART_LSR_DR; ++ serial_out(up, UART_IER, up->ier); ++} ++ ++static void serial8250_enable_ms(struct uart_port *port) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ ++ up->ier |= UART_IER_MSI; ++ serial_out(up, UART_IER, up->ier); ++} ++ ++static _INLINE_ void ++receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs) ++{ ++ struct tty_struct *tty = up->port.info->tty; ++ unsigned char ch; ++ int max_count = 256; ++ ++ do { ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) { ++ /* ++ * FIXME: Deadlock can happen here if we're a ++ * low-latency port. We're holding the per-port ++ * spinlock, and we call flush_to_ldisc-> ++ * n_tty_receive_buf->n_tty_receive_char-> ++ * opost->uart_put_char. ++ */ ++ tty->flip.tqueue.routine((void *)tty); ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) ++ return; // if TTY_DONT_FLIP is set ++ } ++ ch = serial_inp(up, UART_RX); ++ *tty->flip.char_buf_ptr = ch; ++ *tty->flip.flag_buf_ptr = TTY_NORMAL; ++ up->port.icount.rx++; ++ ++ if (*status & (UART_LSR_BI | UART_LSR_PE | ++ UART_LSR_FE | UART_LSR_OE)) { ++ /* ++ * For statistics only ++ */ ++ if (*status & UART_LSR_BI) { ++ *status &= ~(UART_LSR_FE | UART_LSR_PE); ++ up->port.icount.brk++; ++ /* ++ * We do the SysRQ and SAK checking ++ * here because otherwise the break ++ * may get masked by ignore_status_mask ++ * or read_status_mask. ++ */ ++ if (uart_handle_break(&up->port)) ++ goto ignore_char; ++ } else if (*status & UART_LSR_PE) ++ up->port.icount.parity++; ++ else if (*status & UART_LSR_FE) ++ up->port.icount.frame++; ++ if (*status & UART_LSR_OE) ++ up->port.icount.overrun++; ++ ++ /* ++ * Mask off conditions which should be ingored. ++ */ ++ *status &= up->port.read_status_mask; ++ ++#ifdef CONFIG_SERIAL_8250_CONSOLE ++ if (up->port.line == up->port.cons->index) { ++ /* Recover the break flag from console xmit */ ++ *status |= up->lsr_break_flag; ++ up->lsr_break_flag = 0; ++ } ++#endif ++ if (*status & UART_LSR_BI) { ++ DEBUG_INTR("handling break...."); ++ *tty->flip.flag_buf_ptr = TTY_BREAK; ++ } else if (*status & UART_LSR_PE) ++ *tty->flip.flag_buf_ptr = TTY_PARITY; ++ else if (*status & UART_LSR_FE) ++ *tty->flip.flag_buf_ptr = TTY_FRAME; ++ } ++ if (uart_handle_sysrq_char(&up->port, ch, regs)) ++ goto ignore_char; ++ if ((*status & up->port.ignore_status_mask) == 0) { ++ tty->flip.flag_buf_ptr++; ++ tty->flip.char_buf_ptr++; ++ tty->flip.count++; ++ } ++ if ((*status & UART_LSR_OE) && ++ tty->flip.count < TTY_FLIPBUF_SIZE) { ++ /* ++ * Overrun is special, since it's reported ++ * immediately, and doesn't affect the current ++ * character. ++ */ ++ *tty->flip.flag_buf_ptr = TTY_OVERRUN; ++ tty->flip.flag_buf_ptr++; ++ tty->flip.char_buf_ptr++; ++ tty->flip.count++; ++ } ++ ignore_char: ++ *status = serial_inp(up, UART_LSR); ++ } while ((*status & UART_LSR_DR) && (max_count-- > 0)); ++ tty_flip_buffer_push(tty); ++} ++ ++static _INLINE_ void transmit_chars(struct uart_8250_port *up) ++{ ++ struct circ_buf *xmit = &up->port.info->xmit; ++ int count; ++ ++ if (up->port.x_char) { ++ serial_outp(up, UART_TX, up->port.x_char); ++ up->port.icount.tx++; ++ up->port.x_char = 0; ++ return; ++ } ++ if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { ++ serial8250_stop_tx(&up->port, 0); ++ return; ++ } ++ ++ count = up->port.fifosize; ++ do { ++ serial_out(up, UART_TX, xmit->buf[xmit->tail]); ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ up->port.icount.tx++; ++ if (uart_circ_empty(xmit)) ++ break; ++ } while (--count > 0); ++ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(&up->port); ++ ++ DEBUG_INTR("THRE..."); ++ ++ if (uart_circ_empty(xmit)) ++ serial8250_stop_tx(&up->port, 0); ++} ++ ++static _INLINE_ void check_modem_status(struct uart_8250_port *up) ++{ ++ int status; ++ ++ status = serial_in(up, UART_MSR); ++ ++ if ((status & UART_MSR_ANY_DELTA) == 0) ++ return; ++ ++ if (status & UART_MSR_TERI) ++ up->port.icount.rng++; ++ if (status & UART_MSR_DDSR) ++ up->port.icount.dsr++; ++ if (status & UART_MSR_DDCD) ++ uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); ++ if (status & UART_MSR_DCTS) ++ uart_handle_cts_change(&up->port, status & UART_MSR_CTS); ++ ++ wake_up_interruptible(&up->port.info->delta_msr_wait); ++} ++ ++/* ++ * This handles the interrupt from one port. ++ */ ++static inline void ++serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs) ++{ ++ unsigned int status = serial_inp(up, UART_LSR); ++ ++ DEBUG_INTR("status = %x...", status); ++ ++ if (status & UART_LSR_DR) ++ receive_chars(up, &status, regs); ++ check_modem_status(up); ++ if (status & UART_LSR_THRE) ++ transmit_chars(up); ++} ++ ++/* ++ * This is the serial driver's interrupt routine. ++ * ++ * Arjan thinks the old way was overly complex, so it got simplified. ++ * Alan disagrees, saying that need the complexity to handle the weird ++ * nature of ISA shared interrupts. (This is a special exception.) ++ * ++ * In order to handle ISA shared interrupts properly, we need to check ++ * that all ports have been serviced, and therefore the ISA interrupt ++ * line has been de-asserted. ++ * ++ * This means we need to loop through all ports. checking that they ++ * don't have an interrupt pending. ++ */ ++static void serial8250_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct irq_info *i = dev_id; ++ struct list_head *l, *end = NULL; ++ int pass_counter = 0; ++ ++ DEBUG_INTR("serial8250_interrupt(%d)...", irq); ++ ++ spin_lock(&i->lock); ++ ++ l = i->head; ++ do { ++ struct uart_8250_port *up; ++ unsigned int iir; ++ ++ up = list_entry(l, struct uart_8250_port, list); ++ ++ iir = serial_in(up, UART_IIR); ++ if (!(iir & UART_IIR_NO_INT)) { ++ spin_lock(&up->port.lock); ++ serial8250_handle_port(up, regs); ++ spin_unlock(&up->port.lock); ++ ++ end = NULL; ++ } else if (end == NULL) ++ end = l; ++ ++ l = l->next; ++ ++ if (l == i->head && pass_counter++ > PASS_LIMIT) { ++ /* If we hit this, we're dead. */ ++ printk(KERN_ERR "serial8250: too much work for " ++ "irq%d\n", irq); ++ break; ++ } ++ } while (l != end); ++ ++ spin_unlock(&i->lock); ++ ++ DEBUG_INTR("end.\n"); ++} ++ ++/* ++ * To support ISA shared interrupts, we need to have one interrupt ++ * handler that ensures that the IRQ line has been deasserted ++ * before returning. Failing to do this will result in the IRQ ++ * line being stuck active, and, since ISA irqs are edge triggered, ++ * no more IRQs will be seen. ++ */ ++static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up) ++{ ++ spin_lock_irq(&i->lock); ++ ++ if (!list_empty(i->head)) { ++ if (i->head == &up->list) ++ i->head = i->head->next; ++ list_del(&up->list); ++ } else { ++ BUG_ON(i->head != &up->list); ++ i->head = NULL; ++ } ++ ++ spin_unlock_irq(&i->lock); ++} ++ ++static int serial_link_irq_chain(struct uart_8250_port *up) ++{ ++ struct irq_info *i = irq_lists + up->port.irq; ++ int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? SA_SHIRQ : 0; ++ ++ spin_lock_irq(&i->lock); ++ ++ if (i->head) { ++ list_add(&up->list, i->head); ++ spin_unlock_irq(&i->lock); ++ ++ ret = 0; ++ } else { ++ INIT_LIST_HEAD(&up->list); ++ i->head = &up->list; ++ spin_unlock_irq(&i->lock); ++ ++ ret = request_irq(up->port.irq, serial8250_interrupt, ++ irq_flags, "serial", i); ++ if (ret < 0) ++ serial_do_unlink(i, up); ++ } ++ ++ return ret; ++} ++ ++static void serial_unlink_irq_chain(struct uart_8250_port *up) ++{ ++ struct irq_info *i = irq_lists + up->port.irq; ++ ++ BUG_ON(i->head == NULL); ++ ++ if (list_empty(i->head)) ++ free_irq(up->port.irq, i); ++ ++ serial_do_unlink(i, up); ++} ++ ++/* ++ * This function is used to handle ports that do not have an ++ * interrupt. This doesn't work very well for 16450's, but gives ++ * barely passable results for a 16550A. (Although at the expense ++ * of much CPU overhead). ++ */ ++static void serial8250_timeout(unsigned long data) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)data; ++ unsigned int timeout; ++ unsigned int iir; ++ ++ iir = serial_in(up, UART_IIR); ++ if (!(iir & UART_IIR_NO_INT)) { ++ spin_lock(&up->port.lock); ++ serial8250_handle_port(up, NULL); ++ spin_unlock(&up->port.lock); ++ } ++ ++ timeout = up->port.timeout; ++ timeout = timeout > 6 ? (timeout / 2 - 2) : 1; ++ mod_timer(&up->timer, jiffies + timeout); ++} ++ ++static unsigned int serial8250_tx_empty(struct uart_port *port) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ unsigned long flags; ++ unsigned int ret; ++ ++ spin_lock_irqsave(&up->port.lock, flags); ++ ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; ++ spin_unlock_irqrestore(&up->port.lock, flags); ++ ++ return ret; ++} ++ ++static unsigned int serial8250_get_mctrl(struct uart_port *port) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ unsigned long flags; ++ unsigned char status; ++ unsigned int ret; ++ ++ spin_lock_irqsave(&up->port.lock, flags); ++ status = serial_in(up, UART_MSR); ++ spin_unlock_irqrestore(&up->port.lock, flags); ++ ++ ret = 0; ++ if (status & UART_MSR_DCD) ++ ret |= TIOCM_CAR; ++ if (status & UART_MSR_RI) ++ ret |= TIOCM_RNG; ++ if (status & UART_MSR_DSR) ++ ret |= TIOCM_DSR; ++ if (status & UART_MSR_CTS) ++ ret |= TIOCM_CTS; ++ return ret; ++} ++ ++static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ unsigned char mcr = 0; ++ ++ if (mctrl & TIOCM_RTS) ++ mcr |= UART_MCR_RTS; ++ if (mctrl & TIOCM_DTR) ++ mcr |= UART_MCR_DTR; ++ if (mctrl & TIOCM_OUT1) ++ mcr |= UART_MCR_OUT1; ++ if (mctrl & TIOCM_OUT2) ++ mcr |= UART_MCR_OUT2; ++ if (mctrl & TIOCM_LOOP) ++ mcr |= UART_MCR_LOOP; ++ ++ mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr; ++ ++ serial_out(up, UART_MCR, mcr); ++} ++ ++static void serial8250_break_ctl(struct uart_port *port, int break_state) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&up->port.lock, flags); ++ if (break_state == -1) ++ up->lcr |= UART_LCR_SBC; ++ else ++ up->lcr &= ~UART_LCR_SBC; ++ serial_out(up, UART_LCR, up->lcr); ++ spin_unlock_irqrestore(&up->port.lock, flags); ++} ++ ++static int serial8250_startup(struct uart_port *port) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ unsigned long flags; ++ unsigned char lsr, iir; ++ int retval; ++ ++ up->capabilities = uart_config[up->port.type].flags; ++ up->mcr = 0; ++ up->efr = 0; ++ up->ier = 0; ++ ++ if (up->port.type == PORT_16C950) { ++ /* Wake up and initialize UART */ ++ up->acr = 0; ++ serial_outp(up, UART_LCR, 0xBF); ++ serial_outp(up, UART_EFR, UART_EFR_ECB); ++ serial_outp(up, UART_IER, 0); ++ serial_outp(up, UART_LCR, 0); ++ serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ ++ serial_outp(up, UART_LCR, 0xBF); ++ serial_outp(up, UART_EFR, UART_EFR_ECB); ++ serial_outp(up, UART_LCR, 0); ++ } ++ ++#ifdef CONFIG_SERIAL_8250_RSA ++ /* ++ * If this is an RSA port, see if we can kick it up to the ++ * higher speed clock. ++ */ ++ enable_rsa(up); ++#endif ++ ++ /* ++ * Clear the FIFO buffers and disable them. ++ * (they will be reeanbled in change_speed()) ++ */ ++ if (up->capabilities & UART_CLEAR_FIFO) { ++ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); ++ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | ++ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); ++ serial_outp(up, UART_FCR, 0); ++ } ++ ++ /* ++ * Clear the interrupt registers. ++ */ ++ (void) serial_inp(up, UART_LSR); ++ (void) serial_inp(up, UART_RX); ++ (void) serial_inp(up, UART_IIR); ++ (void) serial_inp(up, UART_MSR); ++ ++ /* ++ * At this point, there's no way the LSR could still be 0xff; ++ * if it is, then bail out, because there's likely no UART ++ * here. ++ */ ++ if (!(up->port.flags & UPF_BUGGY_UART) && ++ (serial_inp(up, UART_LSR) == 0xff)) { ++ printk("ttyS%d: LSR safety check engaged!\n", up->port.line); ++ return -ENODEV; ++ } ++ ++ /* ++ * If the "interrupt" for this port doesn't correspond with any ++ * hardware interrupt, we use a timer-based system. The original ++ * driver used to do this with IRQ0. ++ */ ++ if (!is_real_interrupt(up->port.irq)) { ++ unsigned int timeout = up->port.timeout; ++ ++ timeout = timeout > 6 ? (timeout / 2 - 2) : 1; ++ ++ up->timer.data = (unsigned long)up; ++ mod_timer(&up->timer, jiffies + timeout); ++ } else { ++ retval = serial_link_irq_chain(up); ++ if (retval) ++ return retval; ++ } ++ ++ /* ++ * Now, initialize the UART ++ */ ++ serial_outp(up, UART_LCR, UART_LCR_WLEN8); ++ ++ spin_lock_irqsave(&up->port.lock, flags); ++ if (up->port.flags & UPF_FOURPORT) { ++ if (!is_real_interrupt(up->port.irq)) ++ up->port.mctrl |= TIOCM_OUT1; ++ } else ++ /* ++ * Most PC uarts need OUT2 raised to enable interrupts. ++ */ ++ if (is_real_interrupt(up->port.irq)) ++ up->port.mctrl |= TIOCM_OUT2; ++ ++ serial8250_set_mctrl(&up->port, up->port.mctrl); ++ ++ /* ++ * Do a quick test to see if we receive an ++ * interrupt when we enable the TX irq. ++ */ ++ serial_outp(up, UART_IER, UART_IER_THRI); ++ lsr = serial_in(up, UART_LSR); ++ iir = serial_in(up, UART_IIR); ++ serial_outp(up, UART_IER, 0); ++ ++ if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { ++ up->capabilities |= UART_BAD_TX_ENABLE; ++ printk("ttyS%d - enabling bad tx status workarounds\n", ++ port->line); ++ } ++ ++ spin_unlock_irqrestore(&up->port.lock, flags); ++ ++ /* ++ * Finally, enable interrupts. Note: Modem status interrupts ++ * are set via change_speed(), which will be occuring imminently ++ * anyway, so we don't enable them here. ++ */ ++ up->ier = UART_IER_RLSI | UART_IER_RDI; ++ serial_outp(up, UART_IER, up->ier); ++ ++ if (up->port.flags & UPF_FOURPORT) { ++ unsigned int icp; ++ /* ++ * Enable interrupts on the AST Fourport board ++ */ ++ icp = (up->port.iobase & 0xfe0) | 0x01f; ++ outb_p(0x80, icp); ++ (void) inb_p(icp); ++ } ++ ++ /* ++ * And clear the interrupt registers again for luck. ++ */ ++ (void) serial_inp(up, UART_LSR); ++ (void) serial_inp(up, UART_RX); ++ (void) serial_inp(up, UART_IIR); ++ (void) serial_inp(up, UART_MSR); ++ ++ return 0; ++} ++ ++static void serial8250_shutdown(struct uart_port *port) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ unsigned long flags; ++ ++ /* ++ * Disable interrupts from this port ++ */ ++ up->ier = 0; ++ serial_outp(up, UART_IER, 0); ++ ++ spin_lock_irqsave(&up->port.lock, flags); ++ if (up->port.flags & UPF_FOURPORT) { ++ /* reset interrupts on the AST Fourport board */ ++ inb((up->port.iobase & 0xfe0) | 0x1f); ++ up->port.mctrl |= TIOCM_OUT1; ++ } else ++ up->port.mctrl &= ~TIOCM_OUT2; ++ ++ serial8250_set_mctrl(&up->port, up->port.mctrl); ++ spin_unlock_irqrestore(&up->port.lock, flags); ++ ++ /* ++ * Disable break condition and FIFOs ++ */ ++ serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC); ++ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | ++ UART_FCR_CLEAR_RCVR | ++ UART_FCR_CLEAR_XMIT); ++ serial_outp(up, UART_FCR, 0); ++ ++#ifdef CONFIG_SERIAL_8250_RSA ++ /* ++ * Reset the RSA board back to 115kbps compat mode. ++ */ ++ disable_rsa(up); ++#endif ++ ++ /* ++ * Read data port to reset things, and then unlink from ++ * the IRQ chain. ++ */ ++ (void) serial_in(up, UART_RX); ++ ++ if (!is_real_interrupt(up->port.irq)) ++ del_timer_sync(&up->timer); ++ else ++ serial_unlink_irq_chain(up); ++} ++ ++static void serial8250_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ unsigned char cval, fcr = 0; ++ unsigned long flags; ++ ++ switch (cflag & CSIZE) { ++ case CS5: ++ cval = 0x00; ++ break; ++ case CS6: ++ cval = 0x01; ++ break; ++ case CS7: ++ cval = 0x02; ++ break; ++ default: ++ case CS8: ++ cval = 0x03; ++ break; ++ } ++ ++ if (cflag & CSTOPB) ++ cval |= 0x04; ++ if (cflag & PARENB) ++ cval |= UART_LCR_PARITY; ++ if (!(cflag & PARODD)) ++ cval |= UART_LCR_EPAR; ++#ifdef CMSPAR ++ if (cflag & CMSPAR) ++ cval |= UART_LCR_SPAR; ++#endif ++ ++ /* ++ * Work around a bug in the Oxford Semiconductor 952 rev B ++ * chip which causes it to seriously miscalculate baud rates ++ * when DLL is 0. ++ */ ++ if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 && ++ up->rev == 0x5201) ++ quot ++; ++ ++ if (up->capabilities & UART_USE_FIFO) { ++ if ((up->port.uartclk / quot) < (2400 * 16)) ++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; ++#ifdef CONFIG_SERIAL_8250_RSA ++ else if (up->port.type == PORT_RSA) ++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; ++#endif ++ else ++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; ++ } ++ if (up->port.type == PORT_16750) ++ fcr |= UART_FCR7_64BYTE; ++ ++ /* ++ * Ok, we're now changing the port state. Do it with ++ * interrupts disabled. ++ */ ++ spin_lock_irqsave(&up->port.lock, flags); ++ ++ up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; ++ if (iflag & IGNPAR) ++ up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; ++ if (iflag & (BRKINT | PARMRK)) ++ up->port.read_status_mask |= UART_LSR_BI; ++ ++ /* ++ * Characteres to ignore ++ */ ++ up->port.ignore_status_mask = 0; ++ if (iflag & IGNPAR) ++ up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; ++ if (iflag & IGNBRK) { ++ up->port.ignore_status_mask |= UART_LSR_BI; ++ /* ++ * If we're ignoring parity and break indicators, ++ * ignore overruns too (for real raw support). ++ */ ++ if (iflag & IGNPAR) ++ up->port.ignore_status_mask |= UART_LSR_OE; ++ } ++ ++ /* ++ * ignore all characters if CREAD is not set ++ */ ++ if ((cflag & CREAD) == 0) ++ up->port.ignore_status_mask |= UART_LSR_DR; ++ ++ /* ++ * CTS flow control flag and modem status interrupts ++ */ ++ up->ier &= ~UART_IER_MSI; ++ if (UART_ENABLE_MS(&up->port, cflag)) ++ up->ier |= UART_IER_MSI; ++ ++ serial_out(up, UART_IER, up->ier); ++ ++ if (up->capabilities & UART_MCRAFE) { ++ /* ++ * TI16C750 hardware flow control ++ */ ++ up->mcr &= ~UART_MCR_AFE; ++ if (cflag & CRTSCTS) ++ up->mcr |= UART_MCR_AFE; ++ } ++ if (up->capabilities & UART_EFRAFE) { ++ /* ++ * TI16C752/Startech hardware flow control ++ * FIXME: ++ * - TI16C752 requires control thresholds ++ * to be set for auto-RTS. ++ * - We only enable auto-CTS here. ++ * Note: ST16C654 does not allow MCR bit 1 ++ * to override RTS when UART_EFR_RTS is set. ++ */ ++ up->efr &= ~UART_EFR_CTS; ++ if (cflag & CRTSCTS) ++ up->efr |= UART_EFR_CTS; ++ serial_outp(up, UART_LCR, 0xBF); ++ serial_outp(up, UART_EFR, up->efr); ++ } ++ ++ if (up->capabilities & UART_NATSEMI) { ++ /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */ ++ serial_outp(up, UART_LCR, 0xe0); ++ } else { ++ serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ ++ } ++ serial_outp(up, UART_DLL, quot & 0xff); /* LS of divisor */ ++ serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */ ++ if (up->port.type == PORT_16750) ++ serial_outp(up, UART_FCR, fcr); /* set fcr */ ++ serial_outp(up, UART_LCR, cval); /* reset DLAB */ ++ up->lcr = cval; /* Save LCR */ ++ if (up->port.type != PORT_16750) { ++ if (fcr & UART_FCR_ENABLE_FIFO) { ++ /* emulated UARTs (Lucent Venus 167x) need two steps */ ++ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); ++ } ++ serial_outp(up, UART_FCR, fcr); /* set fcr */ ++ } ++ serial8250_set_mctrl(&up->port, up->port.mctrl); ++ spin_unlock_irqrestore(&up->port.lock, flags); ++} ++ ++static void ++serial8250_pm(struct uart_port *port, unsigned int state, ++ unsigned int oldstate) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ if (state) { ++ /* sleep */ ++ if (up->capabilities & UART_STARTECH) { ++ /* Arrange to enter sleep mode */ ++ serial_outp(up, UART_LCR, 0xBF); ++ serial_outp(up, UART_EFR, UART_EFR_ECB); ++ serial_outp(up, UART_LCR, 0); ++ serial_outp(up, UART_IER, UART_IERX_SLEEP); ++ serial_outp(up, UART_LCR, 0xBF); ++ serial_outp(up, UART_EFR, 0); ++ serial_outp(up, UART_LCR, 0); ++ } ++ if (up->port.type == PORT_16750) { ++ /* Arrange to enter sleep mode */ ++ serial_outp(up, UART_IER, UART_IERX_SLEEP); ++ } ++ } else { ++ /* wake */ ++ if (up->capabilities & UART_STARTECH) { ++ /* Wake up UART */ ++ serial_outp(up, UART_LCR, 0xBF); ++ serial_outp(up, UART_EFR, UART_EFR_ECB); ++ /* ++ * Turn off LCR == 0xBF so we actually set the IER ++ * register on the XR16C850 ++ */ ++ serial_outp(up, UART_LCR, 0); ++ serial_outp(up, UART_IER, 0); ++ /* ++ * Now reset LCR so we can turn off the ECB bit ++ */ ++ serial_outp(up, UART_LCR, 0xBF); ++ serial_outp(up, UART_EFR, 0); ++ /* ++ * For a XR16C850, we need to set the trigger levels ++ */ ++ if (up->port.type == PORT_16850) { ++ unsigned char fctr; ++ ++ fctr = serial_inp(up, UART_FCTR) & ++ ~(UART_FCTR_RX | UART_FCTR_TX); ++ serial_outp(up, UART_FCTR, fctr | ++ UART_FCTR_TRGD | ++ UART_FCTR_RX); ++ serial_outp(up, UART_TRG, UART_TRG_96); ++ serial_outp(up, UART_FCTR, fctr | ++ UART_FCTR_TRGD | ++ UART_FCTR_TX); ++ serial_outp(up, UART_TRG, UART_TRG_96); ++ } ++ serial_outp(up, UART_LCR, 0); ++ } ++ ++ if (up->port.type == PORT_16750) { ++ /* Wake up UART */ ++ serial_outp(up, UART_IER, 0); ++ } ++ } ++} ++ ++/* ++ * Resource handling. This is complicated by the fact that resources ++ * depend on the port type. Maybe we should be claiming the standard ++ * 8250 ports, and then trying to get other resources as necessary? ++ */ ++static int ++serial8250_request_std_resource(struct uart_8250_port *up, struct resource **res) ++{ ++ unsigned int size = 8 << up->port.regshift; ++ int ret = 0; ++ ++ switch (up->port.iotype) { ++ case SERIAL_IO_MEM: ++ if (up->port.mapbase) { ++ *res = request_mem_region(up->port.mapbase, size, "serial"); ++ if (!*res) ++ ret = -EBUSY; ++ } ++ break; ++ ++ case SERIAL_IO_HUB6: ++ case SERIAL_IO_PORT: ++ *res = request_region(up->port.iobase, size, "serial"); ++ if (!*res) ++ ret = -EBUSY; ++ break; ++ } ++ return ret; ++} ++ ++static int ++serial8250_request_rsa_resource(struct uart_8250_port *up, struct resource **res) ++{ ++ unsigned int size = 8 << up->port.regshift; ++ unsigned long start; ++ int ret = 0; ++ ++ switch (up->port.iotype) { ++ case SERIAL_IO_MEM: ++ if (up->port.mapbase) { ++ start = up->port.mapbase; ++ start += UART_RSA_BASE << up->port.regshift; ++ *res = request_mem_region(start, size, "serial-rsa"); ++ if (!*res) ++ ret = -EBUSY; ++ } ++ break; ++ ++ case SERIAL_IO_HUB6: ++ case SERIAL_IO_PORT: ++ start = up->port.iobase; ++ start += UART_RSA_BASE << up->port.regshift; ++ *res = request_region(start, size, "serial-rsa"); ++ if (!*res) ++ ret = -EBUSY; ++ break; ++ } ++ ++ return ret; ++} ++ ++static void serial8250_release_port(struct uart_port *port) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ unsigned long start, offset = 0, size = 0; ++ ++ if (up->port.type == PORT_RSA) { ++ offset = UART_RSA_BASE << up->port.regshift; ++ size = 8; ++ } ++ ++ size <<= up->port.regshift; ++ ++ switch (up->port.iotype) { ++ case SERIAL_IO_MEM: ++ if (up->port.mapbase) { ++ /* ++ * Unmap the area. ++ */ ++ if (up->port.flags & UPF_IOREMAP) { ++ iounmap(up->port.membase); ++ up->port.membase = NULL; ++ } ++ ++ start = up->port.mapbase; ++ ++ if (size) ++ release_mem_region(start + offset, size); ++ release_mem_region(start, 8 << up->port.regshift); ++ } ++ break; ++ ++ case SERIAL_IO_HUB6: ++ case SERIAL_IO_PORT: ++ start = up->port.iobase; ++ ++ if (size) ++ release_region(start + offset, size); ++ release_region(start + offset, 8 << up->port.regshift); ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++static int serial8250_request_port(struct uart_port *port) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ struct resource *res = NULL, *res_rsa = NULL; ++ int ret = 0; ++ ++ if (up->port.flags & UPF_RESOURCES) { ++ if (up->port.type == PORT_RSA) { ++ ret = serial8250_request_rsa_resource(up, &res_rsa); ++ if (ret < 0) ++ return ret; ++ } ++ ++ ret = serial8250_request_std_resource(up, &res); ++ } ++ ++ /* ++ * If we have a mapbase, then request that as well. ++ */ ++ if (ret == 0 && up->port.flags & UPF_IOREMAP) { ++ int size = res->end - res->start + 1; ++ ++ up->port.membase = ioremap(up->port.mapbase, size); ++ if (!up->port.membase) ++ ret = -ENOMEM; ++ } ++ ++ if (ret < 0) { ++ if (res_rsa) ++ release_resource(res_rsa); ++ if (res) ++ release_resource(res); ++ } ++ return ret; ++} ++ ++static void serial8250_config_port(struct uart_port *port, int flags) ++{ ++ struct uart_8250_port *up = (struct uart_8250_port *)port; ++ struct resource *res_std = NULL, *res_rsa = NULL; ++ int probeflags = PROBE_ANY; ++ int ret; ++ ++#ifdef CONFIG_MCA ++ /* ++ * Don't probe for MCA ports on non-MCA machines. ++ */ ++ if (up->port.flags & UPF_BOOT_ONLYMCA && !MCA_bus) ++ return; ++#endif ++ ++ /* ++ * Find the region that we can probe for. This in turn ++ * tells us whether we can probe for the type of port. ++ */ ++ if (up->port.flags & UPF_RESOURCES) { ++ ret = serial8250_request_std_resource(up, &res_std); ++ if (ret < 0) ++ return; ++ ++ ret = serial8250_request_rsa_resource(up, &res_rsa); ++ if (ret < 0) ++ probeflags &= ~PROBE_RSA; ++ } else { ++ probeflags &= ~PROBE_RSA; ++ } ++ ++ if (flags & UART_CONFIG_TYPE) ++ autoconfig(up, probeflags); ++ if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) ++ autoconfig_irq(up); ++ ++ /* ++ * If the port wasn't an RSA port, release the resource. ++ */ ++ if (up->port.type != PORT_RSA && res_rsa) ++ release_resource(res_rsa); ++ ++ if (up->port.type == PORT_UNKNOWN && res_std) ++ release_resource(res_std); ++} ++ ++static int ++serial8250_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ if (ser->irq >= NR_IRQS || ser->irq < 0 || ++ ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || ++ ser->type > PORT_MAX_8250 || ser->type == PORT_CIRRUS || ++ ser->type == PORT_STARTECH) ++ return -EINVAL; ++ return 0; ++} ++ ++static const char * ++serial8250_type(struct uart_port *port) ++{ ++ int type = port->type; ++ ++ if (type >= ARRAY_SIZE(uart_config)) ++ type = 0; ++ return uart_config[type].name; ++} ++ ++static struct uart_ops serial8250_pops = { ++ .tx_empty = serial8250_tx_empty, ++ .set_mctrl = serial8250_set_mctrl, ++ .get_mctrl = serial8250_get_mctrl, ++ .stop_tx = serial8250_stop_tx, ++ .start_tx = serial8250_start_tx, ++ .stop_rx = serial8250_stop_rx, ++ .enable_ms = serial8250_enable_ms, ++ .break_ctl = serial8250_break_ctl, ++ .startup = serial8250_startup, ++ .shutdown = serial8250_shutdown, ++ .change_speed = serial8250_change_speed, ++ .pm = serial8250_pm, ++ .type = serial8250_type, ++ .release_port = serial8250_release_port, ++ .request_port = serial8250_request_port, ++ .config_port = serial8250_config_port, ++ .verify_port = serial8250_verify_port, ++}; ++ ++static struct uart_8250_port serial8250_ports[UART_NR]; ++ ++static void __init serial8250_isa_init_ports(void) ++{ ++ struct uart_8250_port *up; ++ static int first = 1; ++ int i; ++ ++ if (!first) ++ return; ++ first = 0; ++ ++ for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port); ++ i++, up++) { ++ up->port.iobase = old_serial_port[i].port; ++ up->port.irq = irq_cannonicalize(old_serial_port[i].irq); ++ up->port.uartclk = old_serial_port[i].baud_base * 16; ++ up->port.flags = old_serial_port[i].flags | ++ UPF_RESOURCES; ++ up->port.hub6 = old_serial_port[i].hub6; ++ up->port.membase = old_serial_port[i].iomem_base; ++ up->port.iotype = old_serial_port[i].io_type; ++ up->port.regshift = old_serial_port[i].iomem_reg_shift; ++ up->port.ops = &serial8250_pops; ++ ++ if (up->port.iotype == UPIO_MEM && up->port.mapbase) ++ up->port.flags |= UPF_IOREMAP; ++ ++ if (share_irqs) ++ up->port.flags |= UPF_SHARE_IRQ; ++ } ++} ++ ++static void __init serial8250_register_ports(struct uart_driver *drv) ++{ ++ int i; ++ ++ serial8250_isa_init_ports(); ++ ++ for (i = 0; i < UART_NR; i++) { ++ struct uart_8250_port *up = &serial8250_ports[i]; ++ ++ up->port.line = i; ++ up->port.ops = &serial8250_pops; ++ init_timer(&up->timer); ++ up->timer.function = serial8250_timeout; ++ ++ /* ++ * ALPHA_KLUDGE_MCR needs to be killed. ++ */ ++ up->mcr_mask = ~ALPHA_KLUDGE_MCR; ++ up->mcr_force = ALPHA_KLUDGE_MCR; ++ ++ uart_add_one_port(drv, &up->port); ++ } ++} ++ ++#ifdef CONFIG_SERIAL_8250_CONSOLE ++ ++#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) ++ ++/* ++ * Wait for transmitter & holding register to empty ++ */ ++static inline void wait_for_xmitr(struct uart_8250_port *up) ++{ ++ unsigned int status, tmout = 10000; ++ ++ /* Wait up to 10ms for the character(s) to be sent. */ ++ do { ++ status = serial_in(up, UART_LSR); ++ ++ if (status & UART_LSR_BI) ++ up->lsr_break_flag = UART_LSR_BI; ++ ++ if (--tmout == 0) ++ break; ++ udelay(1); ++ } while ((status & BOTH_EMPTY) != BOTH_EMPTY); ++ ++ /* Wait up to 1s for flow control if necessary */ ++ if (up->port.flags & UPF_CONS_FLOW) { ++ tmout = 1000000; ++ while (--tmout && ++ ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) ++ udelay(1); ++ } ++} ++ ++/* ++ * Print a string to the serial port trying not to disturb ++ * any possible real use of the port... ++ * ++ * The console_lock must be held when we get here. ++ */ ++static void ++serial8250_console_write(struct console *co, const char *s, unsigned int count) ++{ ++ struct uart_8250_port *up = &serial8250_ports[co->index]; ++ unsigned int ier; ++ int i; ++ ++ /* ++ * First save the UER then disable the interrupts ++ */ ++ ier = serial_in(up, UART_IER); ++ serial_out(up, UART_IER, 0); ++ ++ /* ++ * Now, do each character ++ */ ++ for (i = 0; i < count; i++, s++) { ++ wait_for_xmitr(up); ++ ++ /* ++ * Send the character out. ++ * If a LF, also do CR... ++ */ ++ serial_out(up, UART_TX, *s); ++ if (*s == 10) { ++ wait_for_xmitr(up); ++ serial_out(up, UART_TX, 13); ++ } ++ } ++ ++ /* ++ * Finally, wait for transmitter to become empty ++ * and restore the IER ++ */ ++ wait_for_xmitr(up); ++ serial_out(up, UART_IER, ier); ++} ++ ++static kdev_t serial8250_console_device(struct console *co) ++{ ++ return MKDEV(TTY_MAJOR, 64 + co->index); ++} ++ ++static int __init serial8250_console_setup(struct console *co, char *options) ++{ ++ struct uart_port *port; ++ int baud = 9600; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++ ++ /* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++ if (co->index >= UART_NR) ++ co->index = 0; ++ port = &serial8250_ports[co->index].port; ++ ++ /* ++ * Temporary fix. ++ */ ++ spin_lock_init(&port->lock); ++ ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ ++ return uart_set_options(port, co, baud, parity, bits, flow); ++} ++ ++static struct console serial8250_console = { ++ .name = "ttyS", ++ .write = serial8250_console_write, ++ .device = serial8250_console_device, ++ .setup = serial8250_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++}; ++ ++void __init serial8250_console_init(void) ++{ ++ serial8250_isa_init_ports(); ++ register_console(&serial8250_console); ++} ++ ++#define SERIAL8250_CONSOLE &serial8250_console ++#else ++#define SERIAL8250_CONSOLE NULL ++#endif ++ ++static struct uart_driver serial8250_reg = { ++ .owner = THIS_MODULE, ++#ifdef CONFIG_DEVFS_FS ++ .normal_name = "tts/%d", ++ .callout_name = "cua/%d", ++#else ++ .normal_name = "ttyS", ++ .callout_name = "cua", ++#endif ++ .normal_major = TTY_MAJOR, ++ .callout_major = TTYAUX_MAJOR, ++ .normal_driver = &normal, ++ .callout_driver = &callout, ++ .table = serial8250_table, ++ .termios = serial8250_termios, ++ .termios_locked = serial8250_termios_locked, ++ .minor = 64, ++ .nr = UART_NR, ++ .cons = SERIAL8250_CONSOLE, ++}; ++ ++/* ++ * register_serial and unregister_serial allows for 16x50 serial ports to be ++ * configured at run-time, to support PCMCIA modems. ++ */ ++ ++static int __register_serial(struct serial_struct *req, int line) ++{ ++ struct uart_port port; ++ ++ port.iobase = req->port; ++ port.membase = req->iomem_base; ++ port.irq = req->irq; ++ port.uartclk = req->baud_base * 16; ++ port.fifosize = req->xmit_fifo_size; ++ port.regshift = req->iomem_reg_shift; ++ port.iotype = req->io_type; ++ port.flags = req->flags | UPF_BOOT_AUTOCONF; ++ port.mapbase = req->iomap_base; ++ port.line = line; ++ ++ if (share_irqs) ++ port.flags |= UPF_SHARE_IRQ; ++ ++ if (HIGH_BITS_OFFSET) ++ port.iobase |= (long) req->port_high << HIGH_BITS_OFFSET; ++ ++ /* ++ * If a clock rate wasn't specified by the low level ++ * driver, then default to the standard clock rate. ++ */ ++ if (port.uartclk == 0) ++ port.uartclk = BASE_BAUD * 16; ++ ++ return uart_register_port(&serial8250_reg, &port); ++} ++ ++/** ++ * register_serial - configure a 16x50 serial port at runtime ++ * @req: request structure ++ * ++ * Configure the serial port specified by the request. If the ++ * port exists and is in use an error is returned. If the port ++ * is not currently in the table it is added. ++ * ++ * The port is then probed and if necessary the IRQ is autodetected ++ * If this fails an error is returned. ++ * ++ * On success the port is ready to use and the line number is returned. ++ */ ++int register_serial(struct serial_struct *req) ++{ ++ return __register_serial(req, -1); ++} ++ ++/** ++ * unregister_serial - remove a 16x50 serial port at runtime ++ * @line: serial line number ++ * ++ * Remove one serial port. This may be called from interrupt ++ * context. ++ */ ++void unregister_serial(int line) ++{ ++ uart_unregister_port(&serial8250_reg, line); ++} ++ ++/* ++ * This is for ISAPNP only. ++ */ ++void serial8250_get_irq_map(unsigned int *map) ++{ ++ int i; ++ ++ for (i = 0; i < UART_NR; i++) { ++ if (serial8250_ports[i].port.type != PORT_UNKNOWN && ++ serial8250_ports[i].port.irq < 16) ++ *map |= 1 << serial8250_ports[i].port.irq; ++ } ++} ++ ++static int __init serial8250_init(void) ++{ ++ int ret, i; ++ ++ for (i = 0; i < NR_IRQS; i++) ++ spin_lock_init(&irq_lists[i].lock); ++ ++ ret = uart_register_driver(&serial8250_reg); ++ if (ret >= 0) ++ serial8250_register_ports(&serial8250_reg); ++ ++ return ret; ++} ++ ++static void __exit serial8250_exit(void) ++{ ++ int i; ++ ++ for (i = 0; i < UART_NR; i++) ++ uart_remove_one_port(&serial8250_reg, &serial8250_ports[i].port); ++ ++ uart_unregister_driver(&serial8250_reg); ++} ++ ++module_init(serial8250_init); ++module_exit(serial8250_exit); ++ ++EXPORT_SYMBOL(register_serial); ++EXPORT_SYMBOL(unregister_serial); ++EXPORT_SYMBOL(serial8250_get_irq_map); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Generic 8250/16x50 serial driver"); ++ ++MODULE_PARM(share_irqs, "i"); ++MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices" ++ " (unsafe)"); ++ ++#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE) ++MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); ++MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); ++MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); ++MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); ++#endif /* CONFIG_SERIAL_8250_RSA */ ++ +diff -urN linux-2.4.26/drivers/serial/8250.h linux-2.4.26-vrs1/drivers/serial/8250.h +--- linux-2.4.26/drivers/serial/8250.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/8250.h 2004-04-19 16:49:20.000000000 +0100 +@@ -0,0 +1,88 @@ ++/* ++ * linux/drivers/serial/8250.h ++ * ++ * Driver for 8250/16550-type serial ports ++ * ++ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. ++ * ++ * Copyright (C) 2001 Russell King. ++ * ++ * 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. ++ * ++ * $Id: 8250.h,v 1.1.1.1.2.1 2002/10/24 09:53:24 rmk Exp $ ++ */ ++ ++#include ++ ++struct serial8250_probe { ++ struct module *owner; ++ int (*pci_init_one)(struct pci_dev *dev); ++ void (*pci_remove_one)(struct pci_dev *dev); ++ void (*pnp_init)(void); ++}; ++ ++int serial8250_register_probe(struct serial8250_probe *probe); ++void serial8250_unregister_probe(struct serial8250_probe *probe); ++void serial8250_get_irq_map(unsigned int *map); ++ ++struct old_serial_port { ++ unsigned int uart; ++ unsigned int baud_base; ++ unsigned int port; ++ unsigned int irq; ++ unsigned int flags; ++ unsigned char hub6; ++ unsigned char io_type; ++ unsigned char *iomem_base; ++ unsigned short iomem_reg_shift; ++}; ++ ++struct serial8250_config { ++ const char *name; ++ unsigned int dfl_xmit_fifo_size; ++ unsigned int flags; ++}; ++ ++#define UART_CLEAR_FIFO 0x01 ++#define UART_USE_FIFO 0x02 ++#define UART_STARTECH 0x04 ++#define UART_NATSEMI 0x08 ++#define UART_MCRAFE 0x10 /* TI16C750-style auto-flow */ ++#define UART_EFRAFE 0x20 /* TI16C752/startech auto-flow */ ++ ++#define UART_BAD_TX_ENABLE 0x80000000 ++ ++#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) ++#define SERIAL_INLINE ++#endif ++ ++#ifdef SERIAL_INLINE ++#define _INLINE_ inline ++#else ++#define _INLINE_ ++#endif ++ ++#define PROBE_RSA (1 << 0) ++#define PROBE_ANY (~0) ++ ++#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) ++ ++#ifdef CONFIG_SERIAL_8250_SHARE_IRQ ++#define SERIAL8250_SHARE_IRQS 1 ++#else ++#define SERIAL8250_SHARE_IRQS 0 ++#endif ++ ++#if defined(__alpha__) && !defined(CONFIG_PCI) ++/* ++ * Digital did something really horribly wrong with the OUT1 and OUT2 ++ * lines on at least some ALPHA's. The failure mode is that if either ++ * is cleared, the machine locks up with endless interrupts. ++ */ ++#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1) ++#else ++#define ALPHA_KLUDGE_MCR 0 ++#endif +diff -urN linux-2.4.26/drivers/serial/8250_pci.c linux-2.4.26-vrs1/drivers/serial/8250_pci.c +--- linux-2.4.26/drivers/serial/8250_pci.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/8250_pci.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,1080 @@ ++/* ++ * linux/drivers/char/serial_8250_pci.c ++ * ++ * Probe module for 8250/16550-type PCI serial ports. ++ * ++ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * 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. ++ * ++ * $Id: 8250_pci.c,v 1.8.2.1 2002/10/24 09:53:24 rmk Exp $ ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 2.4.6 compatibility cruft ;( */ ++#define pci_board __pci_board ++#include ++#undef pci_board ++ ++#include ++#include ++#include ++ ++#include "8250.h" ++ ++#ifndef IS_PCI_REGION_IOPORT ++#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ ++ IORESOURCE_IO) ++#endif ++#ifndef IS_PCI_REGION_IOMEM ++#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ ++ IORESOURCE_MEM) ++#endif ++#ifndef PCI_IRQ_RESOURCE ++#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) ++#endif ++ ++#ifndef pci_get_subvendor ++#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) ++#define pci_get_subdevice(dev) ((dev)->subsystem_device) ++#endif ++ ++struct serial_private { ++ unsigned int nr; ++ struct pci_board *board; ++ int line[0]; ++}; ++ ++struct pci_board { ++ int flags; ++ int num_ports; ++ int base_baud; ++ int uart_offset; ++ int reg_shift; ++ int (*init_fn)(struct pci_dev *dev, struct pci_board *board, ++ int enable); ++ int first_uart_offset; ++}; ++ ++static int ++get_pci_port(struct pci_dev *dev, struct pci_board *board, ++ struct serial_struct *req, int idx) ++{ ++ unsigned long port; ++ int base_idx; ++ int max_port; ++ int offset; ++ ++ base_idx = SPCI_FL_GET_BASE(board->flags); ++ if (board->flags & SPCI_FL_BASE_TABLE) ++ base_idx += idx; ++ ++ if (board->flags & SPCI_FL_REGION_SZ_CAP) { ++ max_port = pci_resource_len(dev, base_idx) / 8; ++ if (idx >= max_port) ++ return 1; ++ } ++ ++ offset = board->first_uart_offset; ++ ++ /* Timedia/SUNIX uses a mixture of BARs and offsets */ ++ /* Ugh, this is ugly as all hell --- TYT */ ++ if(dev->vendor == PCI_VENDOR_ID_TIMEDIA ) /* 0x1409 */ ++ switch(idx) { ++ case 0: base_idx=0; ++ break; ++ case 1: base_idx=0; offset=8; ++ break; ++ case 2: base_idx=1; ++ break; ++ case 3: base_idx=1; offset=8; ++ break; ++ case 4: /* BAR 2*/ ++ case 5: /* BAR 3 */ ++ case 6: /* BAR 4*/ ++ case 7: base_idx=idx-2; /* BAR 5*/ ++ } ++ ++ /* Some Titan cards are also a little weird */ ++ if (dev->vendor == PCI_VENDOR_ID_TITAN && ++ (dev->device == PCI_DEVICE_ID_TITAN_400L || ++ dev->device == PCI_DEVICE_ID_TITAN_800L)) { ++ switch (idx) { ++ case 0: base_idx = 1; ++ break; ++ case 1: base_idx = 2; ++ break; ++ default: ++ base_idx = 4; ++ offset = 8 * (idx - 2); ++ } ++ } ++ ++ port = pci_resource_start(dev, base_idx) + offset; ++ ++ if ((board->flags & SPCI_FL_BASE_TABLE) == 0) ++ port += idx * (board->uart_offset ? board->uart_offset : 8); ++ ++ if (IS_PCI_REGION_IOPORT(dev, base_idx)) { ++ req->port = port; ++ if (HIGH_BITS_OFFSET) ++ req->port_high = port >> HIGH_BITS_OFFSET; ++ else ++ req->port_high = 0; ++ return 0; ++ } ++ req->io_type = SERIAL_IO_MEM; ++ req->iomem_base = ioremap(port, board->uart_offset); ++ req->iomem_reg_shift = board->reg_shift; ++ req->port = 0; ++ return 0; ++} ++ ++static _INLINE_ int get_pci_irq(struct pci_dev *dev, ++ struct pci_board *board, ++ int idx) ++{ ++ int base_idx; ++ ++ if ((board->flags & SPCI_FL_IRQRESOURCE) == 0) ++ return dev->irq; ++ ++ base_idx = SPCI_FL_GET_IRQBASE(board->flags); ++ if (board->flags & SPCI_FL_IRQ_TABLE) ++ base_idx += idx; ++ ++ return PCI_IRQ_RESOURCE(dev, base_idx); ++} ++ ++/* ++ * Some PCI serial cards using the PLX 9050 PCI interface chip require ++ * that the card interrupt be explicitly enabled or disabled. This ++ * seems to be mainly needed on card using the PLX which also use I/O ++ * mapped memory. ++ */ ++static int __devinit ++pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable) ++{ ++ u8 data, *p, irq_config; ++ int pci_config; ++ ++ irq_config = 0x41; ++ pci_config = PCI_COMMAND_MEMORY; ++ if (dev->vendor == PCI_VENDOR_ID_PANACOM) ++ irq_config = 0x43; ++ if ((dev->vendor == PCI_VENDOR_ID_PLX) && ++ (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { ++ /* ++ * As the megawolf cards have the int pins active ++ * high, and have 2 UART chips, both ints must be ++ * enabled on the 9050. Also, the UARTS are set in ++ * 16450 mode by default, so we have to enable the ++ * 16C950 'enhanced' mode so that we can use the deep ++ * FIFOs ++ */ ++ irq_config = 0x5b; ++ pci_config = PCI_COMMAND_MEMORY | PCI_COMMAND_IO; ++ } ++ ++ pci_read_config_byte(dev, PCI_COMMAND, &data); ++ ++ if (enable) ++ pci_write_config_byte(dev, PCI_COMMAND, ++ data | pci_config); ++ ++ /* enable/disable interrupts */ ++ p = ioremap(pci_resource_start(dev, 0), 0x80); ++ writel(enable ? irq_config : 0x00, (unsigned long)p + 0x4c); ++ iounmap(p); ++ ++ if (!enable) ++ pci_write_config_byte(dev, PCI_COMMAND, ++ data & ~pci_config); ++ return 0; ++} ++ ++ ++/* ++ * SIIG serial cards have an PCI interface chip which also controls ++ * the UART clocking frequency. Each UART can be clocked independently ++ * (except cards equiped with 4 UARTs) and initial clocking settings ++ * are stored in the EEPROM chip. It can cause problems because this ++ * version of serial driver doesn't support differently clocked UART's ++ * on single PCI card. To prevent this, initialization functions set ++ * high frequency clocking for all UART's on given card. It is safe (I ++ * hope) because it doesn't touch EEPROM settings to prevent conflicts ++ * with other OSes (like M$ DOS). ++ * ++ * SIIG support added by Andrey Panin , 10/1999 ++ * ++ * There is two family of SIIG serial cards with different PCI ++ * interface chip and different configuration methods: ++ * - 10x cards have control registers in IO and/or memory space; ++ * - 20x cards have control registers in standard PCI configuration space. ++ */ ++ ++#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) ++#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) ++ ++static int __devinit ++pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable) ++{ ++ u16 data, *p; ++ ++ if (!enable) return 0; ++ ++ p = ioremap(pci_resource_start(dev, 0), 0x80); ++ ++ switch (dev->device & 0xfff8) { ++ case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ ++ data = 0xffdf; ++ break; ++ case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ ++ data = 0xf7ff; ++ break; ++ default: /* 1S1P, 4S */ ++ data = 0xfffb; ++ break; ++ } ++ ++ writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28); ++ iounmap(p); ++ return 0; ++} ++ ++#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) ++#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) ++ ++static int __devinit ++pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable) ++{ ++ u8 data; ++ ++ if (!enable) return 0; ++ ++ /* Change clock frequency for the first UART. */ ++ pci_read_config_byte(dev, 0x6f, &data); ++ pci_write_config_byte(dev, 0x6f, data & 0xef); ++ ++ /* If this card has 2 UART, we have to do the same with second UART. */ ++ if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) || ++ ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) { ++ pci_read_config_byte(dev, 0x73, &data); ++ pci_write_config_byte(dev, 0x73, data & 0xef); ++ } ++ return 0; ++} ++ ++/* Added for EKF Intel i960 serial boards */ ++static int __devinit ++pci_inteli960ni_fn(struct pci_dev *dev, ++ struct pci_board *board, ++ int enable) ++{ ++ unsigned long oldval; ++ ++ if (!(pci_get_subdevice(dev) & 0x1000)) ++ return(-1); ++ ++ if (!enable) /* is there something to deinit? */ ++ return(0); ++ ++ /* is firmware started? */ ++ pci_read_config_dword(dev, 0x44, (void*) &oldval); ++ if (oldval == 0x00001000L) { /* RESET value */ ++ printk(KERN_DEBUG "Local i960 firmware missing"); ++ return(-1); ++ } ++ return(0); ++} ++ ++/* ++ * Timedia has an explosion of boards, and to avoid the PCI table from ++ * growing *huge*, we use this function to collapse some 70 entries ++ * in the PCI table into one, for sanity's and compactness's sake. ++ */ ++static unsigned short timedia_single_port[] = { ++ 0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 }; ++static unsigned short timedia_dual_port[] = { ++ 0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085, ++ 0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, ++ 0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, ++ 0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079, ++ 0xD079, 0 }; ++static unsigned short timedia_quad_port[] = { ++ 0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, ++ 0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, ++ 0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056, ++ 0xB157, 0 }; ++static unsigned short timedia_eight_port[] = { ++ 0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, ++ 0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 }; ++static struct timedia_struct { ++ int num; ++ unsigned short *ids; ++} timedia_data[] = { ++ { 1, timedia_single_port }, ++ { 2, timedia_dual_port }, ++ { 4, timedia_quad_port }, ++ { 8, timedia_eight_port }, ++ { 0, 0 } ++}; ++ ++static int __devinit ++pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable) ++{ ++ int i, j; ++ unsigned short *ids; ++ ++ if (!enable) ++ return 0; ++ ++ for (i=0; timedia_data[i].num; i++) { ++ ids = timedia_data[i].ids; ++ for (j=0; ids[j]; j++) { ++ if (pci_get_subdevice(dev) == ids[j]) { ++ board->num_ports = timedia_data[i].num; ++ return 0; ++ } ++ } ++ } ++ return 0; ++} ++ ++static int __devinit ++pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable) ++{ ++ __set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(HZ/10); ++ return 0; ++} ++ ++/* ++ * This is the configuration table for all of the PCI serial boards ++ * which we support. It is directly indexed by the pci_board_num_t enum ++ * value, which is encoded in the pci_device_id PCI probe table's ++ * driver_data member. ++ */ ++enum pci_board_num_t { ++ pbn_b0_1_115200, ++ pbn_default = 0, ++ ++ pbn_b0_2_115200, ++ pbn_b0_4_115200, ++ ++ pbn_b0_1_921600, ++ pbn_b0_2_921600, ++ pbn_b0_4_921600, ++ ++ pbn_b0_bt_1_115200, ++ pbn_b0_bt_2_115200, ++ pbn_b0_bt_1_460800, ++ pbn_b0_bt_2_460800, ++ ++ pbn_b1_1_115200, ++ pbn_b1_2_115200, ++ pbn_b1_4_115200, ++ pbn_b1_8_115200, ++ ++ pbn_b1_2_921600, ++ pbn_b1_4_921600, ++ pbn_b1_8_921600, ++ ++ pbn_b1_2_1382400, ++ pbn_b1_4_1382400, ++ pbn_b1_8_1382400, ++ ++ pbn_b2_8_115200, ++ pbn_b2_4_460800, ++ pbn_b2_8_460800, ++ pbn_b2_16_460800, ++ pbn_b2_4_921600, ++ pbn_b2_8_921600, ++ ++ pbn_b2_bt_1_115200, ++ pbn_b2_bt_2_115200, ++ pbn_b2_bt_4_115200, ++ pbn_b2_bt_2_921600, ++ ++ pbn_panacom, ++ pbn_panacom2, ++ pbn_panacom4, ++ pbn_plx_romulus, ++ pbn_oxsemi, ++ pbn_timedia, ++ pbn_intel_i960, ++ pbn_sgi_ioc3, ++#ifdef CONFIG_DDB5074 ++ pbn_nec_nile4, ++#endif ++#if 0 ++ pbn_dci_pccom8, ++#endif ++ pbn_xircom_combo, ++ ++ pbn_siig10x_0, ++ pbn_siig10x_1, ++ pbn_siig10x_2, ++ pbn_siig10x_4, ++ pbn_siig20x_0, ++ pbn_siig20x_2, ++ pbn_siig20x_4, ++ ++ pbn_computone_4, ++ pbn_computone_6, ++ pbn_computone_8, ++}; ++ ++static struct pci_board pci_boards[] __devinitdata = { ++ /* ++ * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, ++ * Offset to get to next UART's registers, ++ * Register shift to use for memory-mapped I/O, ++ * Initialization function, first UART offset ++ */ ++ ++ /* Generic serial board, pbn_b0_1_115200, pbn_default */ ++ { SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200, ++ pbn_default */ ++ ++ { SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */ ++ { SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */ ++ ++ { SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */ ++ { SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */ ++ { SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */ ++ ++ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ ++ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ ++ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ ++ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ ++ ++ { SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ ++ { SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ ++ { SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */ ++ { SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */ ++ ++ { SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */ ++ { SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */ ++ { SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */ ++ ++ { SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */ ++ { SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */ ++ { SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */ ++ ++ { SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */ ++ { SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */ ++ { SPCI_FL_BASE2, 8, 460800 }, /* pbn_b2_8_460800 */ ++ { SPCI_FL_BASE2, 16, 460800 }, /* pbn_b2_16_460800 */ ++ { SPCI_FL_BASE2, 4, 921600 }, /* pbn_b2_4_921600 */ ++ { SPCI_FL_BASE2, 8, 921600 }, /* pbn_b2_8_921600 */ ++ ++ { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */ ++ { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */ ++ { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */ ++ { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */ ++ ++ { SPCI_FL_BASE2, 2, 921600, /* IOMEM */ /* pbn_panacom */ ++ 0x400, 7, pci_plx9050_fn }, ++ { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_panacom2 */ ++ 0x400, 7, pci_plx9050_fn }, ++ { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_panacom4 */ ++ 0x400, 7, pci_plx9050_fn }, ++ { SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */ ++ 0x20, 2, pci_plx9050_fn, 0x03 }, ++ /* This board uses the size of PCI Base region 0 to ++ * signal now many ports are available */ ++ { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */ ++ { SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */ ++ 0, 0, pci_timedia_fn }, ++ /* EKF addition for i960 Boards form EKF with serial port */ ++ { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */ ++ 8<<2, 2, pci_inteli960ni_fn, 0x10000}, ++ { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, /* pbn_sgi_ioc3 */ ++ 1, 458333, 0, 0, 0, 0x20178 }, ++#ifdef CONFIG_DDB5074 ++ /* ++ * NEC Vrc-5074 (Nile 4) builtin UART. ++ * Conditionally compiled in since this is a motherboard device. ++ */ ++ { SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */ ++ 64, 3, NULL, 0x300 }, ++#endif ++#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ /* pbn_dci_pccom8 */ ++ { SPCI_FL_BASE3, 8, 115200, 8 }, ++#endif ++ { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */ ++ 0, 0, pci_xircom_fn }, ++ ++ { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */ ++ 0, 0, pci_siig10x_fn }, ++ { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */ ++ 0, 0, pci_siig10x_fn }, ++ { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */ ++ 0, 0, pci_siig10x_fn }, ++ { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */ ++ 0, 0, pci_siig10x_fn }, ++ { SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */ ++ 0, 0, pci_siig20x_fn }, ++ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */ ++ 0, 0, pci_siig20x_fn }, ++ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */ ++ 0, 0, pci_siig20x_fn }, ++ ++ { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */ ++ 0x40, 2, NULL, 0x200 }, ++ { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */ ++ 0x40, 2, NULL, 0x200 }, ++ { SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */ ++ 0x40, 2, NULL, 0x200 }, ++}; ++ ++/* ++ * Given a complete unknown PCI device, try to use some heuristics to ++ * guess what the configuration might be, based on the pitiful PCI ++ * serial specs. Returns 0 on success, 1 on failure. ++ */ ++static int __devinit serial_pci_guess_board(struct pci_dev *dev, ++ struct pci_board *board) ++{ ++ int num_iomem = 0, num_port = 0, first_port = -1; ++ int i; ++ ++ /* ++ * If it is not a communications device or the programming ++ * interface is greater than 6, give up. ++ * ++ * (Should we try to make guesses for multiport serial devices ++ * later?) ++ */ ++ if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && ++ ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || ++ (dev->class & 0xff) > 6) ++ return 1; ++ ++ for (i=0; i < 6; i++) { ++ if (IS_PCI_REGION_IOPORT(dev, i)) { ++ num_port++; ++ if (first_port == -1) ++ first_port = i; ++ } ++ if (IS_PCI_REGION_IOMEM(dev, i)) ++ num_iomem++; ++ } ++ ++ /* ++ * If there is 1 or 0 iomem regions, and exactly one port, use ++ * it. ++ */ ++ if (num_iomem <= 1 && num_port == 1) { ++ board->flags = first_port; ++ return 0; ++ } ++ return 1; ++} ++ ++/* ++ * return -1 to refuse ++ */ ++static int pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) ++{ ++ struct serial_private *priv; ++ struct pci_board *board, tmp; ++ struct serial_struct serial_req; ++ int base_baud, rc, k; ++ ++ board = &pci_boards[ent->driver_data]; ++ ++ rc = pci_enable_device(dev); ++ if (rc) ++ return rc; ++ ++ if (ent->driver_data == pbn_default && ++ serial_pci_guess_board(dev, board)) ++ return -ENODEV; ++ else if (serial_pci_guess_board(dev, &tmp) == 0) { ++ printk(KERN_INFO "Redundant entry in serial pci_table. " ++ "Please send the output of\n" ++ "lspci -vv, this message (%d,%d,%d,%d)\n" ++ "and the manufacturer and name of " ++ "serial board or modem board\n" ++ "to serial-pci-info@lists.sourceforge.net.\n", ++ dev->vendor, dev->device, ++ pci_get_subvendor(dev), pci_get_subdevice(dev)); ++ } ++ ++ ++ priv = kmalloc(sizeof(struct serial_private) + ++ sizeof(unsigned int) * board->num_ports, ++ GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ /* ++ * Run the initialization function, if any ++ */ ++ if (board->init_fn && ((board->init_fn)(dev, board, 1) != 0)) { ++ kfree(priv); ++ return -ENODEV; ++ } ++ ++ base_baud = board->base_baud; ++ if (!base_baud) ++ base_baud = BASE_BAUD; ++ memset(&serial_req, 0, sizeof(serial_req)); ++ for (k=0; k < board->num_ports; k++) { ++ serial_req.irq = get_pci_irq(dev, board, k); ++ if (get_pci_port(dev, board, &serial_req, k)) ++ break; ++#ifdef SERIAL_DEBUG_PCI ++ printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", ++ serial_req.port, serial_req.irq, serial_req.io_type); ++#endif ++ serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; ++ serial_req.baud_base = base_baud; ++ priv->line[k] = register_serial(&serial_req); ++ if (priv->line[k] < 0) ++ break; ++ } ++ ++ priv->board = board; ++ priv->nr = k; ++ ++ pci_set_drvdata(dev, priv); ++ ++ return 0; ++} ++ ++static void pci_remove_one(struct pci_dev *dev) ++{ ++ struct serial_private *priv = pci_get_drvdata(dev); ++ int i; ++ ++ pci_set_drvdata(dev, NULL); ++ ++ for (i = 0; i < priv->nr; i++) ++ unregister_serial(priv->line[i]); ++ ++ priv->board->init_fn(dev, priv->board, 0); ++ ++ kfree(priv); ++} ++ ++static struct pci_device_id serial_pci_tbl[] __devinitdata = { ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, ++ pbn_b1_8_1382400 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, ++ pbn_b1_4_1382400 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, ++ pbn_b1_2_1382400 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, ++ pbn_b1_8_1382400 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, ++ pbn_b1_4_1382400 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, ++ pbn_b1_2_1382400 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, ++ pbn_b1_8_921600 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, ++ pbn_b1_8_921600 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, ++ pbn_b1_4_921600 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, ++ pbn_b1_4_921600 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, ++ pbn_b1_2_921600 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, ++ pbn_b1_8_921600 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, ++ pbn_b1_8_921600 }, ++ { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, ++ PCI_SUBVENDOR_ID_CONNECT_TECH, ++ PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, ++ pbn_b1_4_921600 }, ++ ++ { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b2_bt_1_115200 }, ++ { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b2_bt_2_115200 }, ++ { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b2_bt_4_115200 }, ++ { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b2_bt_2_115200 }, ++ { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b2_bt_4_115200 }, ++ { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b2_8_115200 }, ++ ++ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b2_bt_2_115200 }, ++ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b2_bt_2_921600 }, ++ /* VScom SPCOM800, from sl@s.pl */ ++ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b2_8_921600 }, ++ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b2_4_921600 }, ++ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, ++ PCI_SUBVENDOR_ID_KEYSPAN, ++ PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, ++ pbn_panacom }, ++ { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_panacom4 }, ++ { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_panacom2 }, ++ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, ++ PCI_SUBVENDOR_ID_CHASE_PCIFAST, ++ PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, ++ pbn_b2_4_460800 }, ++ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, ++ PCI_SUBVENDOR_ID_CHASE_PCIFAST, ++ PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, ++ pbn_b2_8_460800 }, ++ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, ++ PCI_SUBVENDOR_ID_CHASE_PCIFAST, ++ PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, ++ pbn_b2_16_460800 }, ++ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, ++ PCI_SUBVENDOR_ID_CHASE_PCIFAST, ++ PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, ++ pbn_b2_16_460800 }, ++ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, ++ PCI_SUBVENDOR_ID_CHASE_PCIRAS, ++ PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, ++ pbn_b2_4_460800 }, ++ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, ++ PCI_SUBVENDOR_ID_CHASE_PCIRAS, ++ PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, ++ pbn_b2_8_460800 }, ++ /* Megawolf Romulus PCI Serial Card, from Mike Hudson */ ++ /* (Exoray@isys.ca) */ ++ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, ++ 0x10b5, 0x106a, 0, 0, ++ pbn_plx_romulus }, ++ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b1_4_115200 }, ++ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b1_2_115200 }, ++ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b1_8_115200 }, ++ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b1_8_115200 }, ++ { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, ++ PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0, ++ pbn_b0_4_921600 }, ++ { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_4_115200 }, ++ { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_2_115200 }, ++ ++ /* Digitan DS560-558, from jimd@esoft.com */ ++ { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b1_1_115200 }, ++ ++ /* 3Com US Robotics 56k Voice Internal PCI model 5610 */ ++ { PCI_VENDOR_ID_USR, 0x1008, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ ++ /* Titan Electronic cards */ ++ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_1_921600 }, ++ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_2_921600 }, ++ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_4_921600 }, ++ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_4_921600 }, ++ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, ++ PCI_ANY_ID, PCI_ANY_ID, ++ SPCI_FL_BASE1, 1, 921600 }, ++ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, ++ PCI_ANY_ID, PCI_ANY_ID, ++ SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, ++ /* The 400L and 800L have a custom hack in get_pci_port */ ++ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, ++ PCI_ANY_ID, PCI_ANY_ID, ++ SPCI_FL_BASE_TABLE, 4, 921600 }, ++ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, ++ PCI_ANY_ID, PCI_ANY_ID, ++ SPCI_FL_BASE_TABLE, 8, 921600 }, ++ ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_0 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_0 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_0 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_1 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_1 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_1 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_2 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_2 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_2 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_2 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_2 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_2 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_4 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_4 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig10x_4 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_0 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_0 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_0 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_0 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_0 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_0 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_0 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_0 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_0 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_2 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_2 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_2 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_2 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_2 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_2 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_4 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_4 }, ++ { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_siig20x_4 }, ++ ++ /* Computone devices submitted by Doug McNash dmcnash@computone.com */ ++ { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, ++ PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, ++ 0, 0, pbn_computone_4 }, ++ { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, ++ PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, ++ 0, 0, pbn_computone_8 }, ++ { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, ++ PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, ++ 0, 0, pbn_computone_6 }, ++ ++ { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi }, ++ { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, ++ PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia }, ++ ++ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_bt_2_115200 }, ++ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_bt_2_115200 }, ++ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_bt_2_115200 }, ++ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_bt_2_460800 }, ++ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_bt_2_460800 }, ++ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_bt_2_460800 }, ++ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_bt_1_115200 }, ++ { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b0_bt_1_460800 }, ++ ++ /* RAStel 2 port modem, gerg@moreton.com.au */ ++ { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_b2_bt_2_115200 }, ++ ++ /* EKF addition for i960 Boards form EKF with serial port */ ++ { PCI_VENDOR_ID_INTEL, 0x1960, ++ 0xE4BF, PCI_ANY_ID, 0, 0, ++ pbn_intel_i960 }, ++ ++ /* Xircom Cardbus/Ethernet combos */ ++ { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_xircom_combo }, ++ ++ /* ++ * Untested PCI modems, sent in from various folks... ++ */ ++ ++ /* Elsa Model 56K PCI Modem, from Andreas Rath */ ++ { PCI_VENDOR_ID_ROCKWELL, 0x1004, ++ 0x1048, 0x1500, 0, 0, ++ pbn_b1_1_115200 }, ++ ++ { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, ++ 0xFF00, 0, 0, 0, ++ pbn_sgi_ioc3 }, ++ ++#ifdef CONFIG_DDB5074 ++ /* ++ * NEC Vrc-5074 (Nile 4) builtin UART. ++ * Conditionally compiled in since this is a motherboard device. ++ */ ++ { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_nec_nile4 }, ++#endif ++ ++#if 0 /* PCI_DEVICE_ID_DCI_PCCOM8 ? */ ++ { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, ++ pbn_dci_pccom8 }, ++#endif ++ ++ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, ++ PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00, }, ++ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, ++ PCI_CLASS_COMMUNICATION_MODEM << 8, 0xffff00, }, ++ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, ++ PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00, }, ++ { 0, } ++}; ++ ++static struct pci_driver serial_pci_driver = { ++ name: "serial", ++ probe: pci_init_one, ++ remove: pci_remove_one, ++ id_table: serial_pci_tbl, ++}; ++ ++static int __init serial8250_pci_init(void) ++{ ++ return pci_module_init(&serial_pci_driver); ++} ++ ++static void __exit serial8250_pci_exit(void) ++{ ++ pci_unregister_driver(&serial_pci_driver); ++} ++ ++module_init(serial8250_pci_init); ++module_exit(serial8250_pci_exit); ++ ++EXPORT_NO_SYMBOLS; ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); ++MODULE_GENERIC_TABLE(pci, serial_pci_tbl); +diff -urN linux-2.4.26/drivers/serial/8250_pnp.c linux-2.4.26-vrs1/drivers/serial/8250_pnp.c +--- linux-2.4.26/drivers/serial/8250_pnp.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/8250_pnp.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,553 @@ ++/* ++ * linux/drivers/char/serial_8250_pnp.c ++ * ++ * Probe module for 8250/16550-type ISAPNP serial ports. ++ * ++ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. ++ * ++ * Copyright (C) 2001 Russell King, All Rights Reserved. ++ * ++ * 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. ++ * ++ * $Id: 8250_pnp.c,v 1.3.2.1 2002/10/24 09:53:25 rmk Exp $ ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "8250.h" ++ ++static struct serial_state rs_table[] = { }; ++#define NR_PORTS 0 ++ ++struct pnpbios_device_id ++{ ++ char id[8]; ++ unsigned long driver_data; ++}; ++ ++static const struct pnpbios_device_id pnp_dev_table[] = { ++ /* Archtek America Corp. */ ++ /* Archtek SmartLink Modem 3334BT Plug & Play */ ++ { "AAC000F", 0 }, ++ /* Anchor Datacomm BV */ ++ /* SXPro 144 External Data Fax Modem Plug & Play */ ++ { "ADC0001", 0 }, ++ /* SXPro 288 External Data Fax Modem Plug & Play */ ++ { "ADC0002", 0 }, ++ /* Rockwell 56K ACF II Fax+Data+Voice Modem */ ++ { "AKY1021", SPCI_FL_NO_SHIRQ }, ++ /* AZT3005 PnP SOUND DEVICE */ ++ { "AZT4001", 0 }, ++ /* Best Data Products Inc. Smart One 336F PnP Modem */ ++ { "BDP3336", 0 }, ++ /* Boca Research */ ++ /* Boca Complete Ofc Communicator 14.4 Data-FAX */ ++ { "BRI0A49", 0 }, ++ /* Boca Research 33,600 ACF Modem */ ++ { "BRI1400", 0 }, ++ /* Boca 33.6 Kbps Internal FD34FSVD */ ++ { "BRI3400", 0 }, ++ /* Boca 33.6 Kbps Internal FD34FSVD */ ++ { "BRI0A49", 0 }, ++ /* Best Data Products Inc. Smart One 336F PnP Modem */ ++ { "BDP3336", 0 }, ++ /* Computer Peripherals Inc */ ++ /* EuroViVa CommCenter-33.6 SP PnP */ ++ { "CPI4050", 0 }, ++ /* Creative Labs */ ++ /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ ++ { "CTL3001", 0 }, ++ /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ ++ { "CTL3011", 0 }, ++ /* Creative */ ++ /* Creative Modem Blaster Flash56 DI5601-1 */ ++ { "DMB1032", 0 }, ++ /* Creative Modem Blaster V.90 DI5660 */ ++ { "DMB2001", 0 }, ++ /* FUJITSU */ ++ /* Fujitsu 33600 PnP-I2 R Plug & Play */ ++ { "FUJ0202", 0 }, ++ /* Fujitsu FMV-FX431 Plug & Play */ ++ { "FUJ0205", 0 }, ++ /* Fujitsu 33600 PnP-I4 R Plug & Play */ ++ { "FUJ0206", 0 }, ++ /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ ++ { "FUJ0209", 0 }, ++ /* Archtek America Corp. */ ++ /* Archtek SmartLink Modem 3334BT Plug & Play */ ++ { "GVC000F", 0 }, ++ /* Hayes */ ++ /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ ++ { "HAY0001", 0 }, ++ /* Hayes Optima 336 V.34 + FAX + Voice PnP */ ++ { "HAY000C", 0 }, ++ /* Hayes Optima 336B V.34 + FAX + Voice PnP */ ++ { "HAY000D", 0 }, ++ /* Hayes Accura 56K Ext Fax Modem PnP */ ++ { "HAY5670", 0 }, ++ /* Hayes Accura 56K Ext Fax Modem PnP */ ++ { "HAY5674", 0 }, ++ /* Hayes Accura 56K Fax Modem PnP */ ++ { "HAY5675", 0 }, ++ /* Hayes 288, V.34 + FAX */ ++ { "HAYF000", 0 }, ++ /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ ++ { "HAYF001", 0 }, ++ /* IBM */ ++ /* IBM Thinkpad 701 Internal Modem Voice */ ++ { "IBM0033", 0 }, ++ /* Intertex */ ++ /* Intertex 28k8 33k6 Voice EXT PnP */ ++ { "IXDC801", 0 }, ++ /* Intertex 33k6 56k Voice EXT PnP */ ++ { "IXDC901", 0 }, ++ /* Intertex 28k8 33k6 Voice SP EXT PnP */ ++ { "IXDD801", 0 }, ++ /* Intertex 33k6 56k Voice SP EXT PnP */ ++ { "IXDD901", 0 }, ++ /* Intertex 28k8 33k6 Voice SP INT PnP */ ++ { "IXDF401", 0 }, ++ /* Intertex 28k8 33k6 Voice SP EXT PnP */ ++ { "IXDF801", 0 }, ++ /* Intertex 33k6 56k Voice SP EXT PnP */ ++ { "IXDF901", 0 }, ++ /* Kortex International */ ++ /* KORTEX 28800 Externe PnP */ ++ { "KOR4522", 0 }, ++ /* KXPro 33.6 Vocal ASVD PnP */ ++ { "KORF661", 0 }, ++ /* Lasat */ ++ /* LASAT Internet 33600 PnP */ ++ { "LAS4040", 0 }, ++ /* Lasat Safire 560 PnP */ ++ { "LAS4540", 0 }, ++ /* Lasat Safire 336 PnP */ ++ { "LAS5440", 0 }, ++ /* Microcom, Inc. */ ++ /* Microcom TravelPorte FAST V.34 Plug & Play */ ++ { "MNP0281", 0 }, ++ /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ ++ { "MNP0336", 0 }, ++ /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ ++ { "MNP0339", 0 }, ++ /* Microcom DeskPorte 28.8P Plug & Play */ ++ { "MNP0342", 0 }, ++ /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ ++ { "MNP0500", 0 }, ++ /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ ++ { "MNP0501", 0 }, ++ /* Microcom DeskPorte 28.8S Internal Plug & Play */ ++ { "MNP0502", 0 }, ++ /* Motorola */ ++ /* Motorola BitSURFR Plug & Play */ ++ { "MOT1105", 0 }, ++ /* Motorola TA210 Plug & Play */ ++ { "MOT1111", 0 }, ++ /* Motorola HMTA 200 (ISDN) Plug & Play */ ++ { "MOT1114", 0 }, ++ /* Motorola BitSURFR Plug & Play */ ++ { "MOT1115", 0 }, ++ /* Motorola Lifestyle 28.8 Internal */ ++ { "MOT1190", 0 }, ++ /* Motorola V.3400 Plug & Play */ ++ { "MOT1501", 0 }, ++ /* Motorola Lifestyle 28.8 V.34 Plug & Play */ ++ { "MOT1502", 0 }, ++ /* Motorola Power 28.8 V.34 Plug & Play */ ++ { "MOT1505", 0 }, ++ /* Motorola ModemSURFR External 28.8 Plug & Play */ ++ { "MOT1509", 0 }, ++ /* Motorola Premier 33.6 Desktop Plug & Play */ ++ { "MOT150A", 0 }, ++ /* Motorola VoiceSURFR 56K External PnP */ ++ { "MOT150F", 0 }, ++ /* Motorola ModemSURFR 56K External PnP */ ++ { "MOT1510", 0 }, ++ /* Motorola ModemSURFR 56K Internal PnP */ ++ { "MOT1550", 0 }, ++ /* Motorola ModemSURFR Internal 28.8 Plug & Play */ ++ { "MOT1560", 0 }, ++ /* Motorola Premier 33.6 Internal Plug & Play */ ++ { "MOT1580", 0 }, ++ /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ ++ { "MOT15B0", 0 }, ++ /* Motorola VoiceSURFR 56K Internal PnP */ ++ { "MOT15F0", 0 }, ++ /* Com 1 */ ++ /* Deskline K56 Phone System PnP */ ++ { "MVX00A1", 0 }, ++ /* PC Rider K56 Phone System PnP */ ++ { "MVX00F2", 0 }, ++ /* Pace 56 Voice Internal Plug & Play Modem */ ++ { "PMC2430", 0 }, ++ /* Generic */ ++ /* Generic standard PC COM port */ ++ { "PNP0500", 0 }, ++ /* Generic 16550A-compatible COM port */ ++ { "PNP0501", 0 }, ++ /* Compaq 14400 Modem */ ++ { "PNPC000", 0 }, ++ /* Compaq 2400/9600 Modem */ ++ { "PNPC001", 0 }, ++ /* Dial-Up Networking Serial Cable between 2 PCs */ ++ { "PNPC031", 0 }, ++ /* Dial-Up Networking Parallel Cable between 2 PCs */ ++ { "PNPC032", 0 }, ++ /* Standard 9600 bps Modem */ ++ { "PNPC100", 0 }, ++ /* Standard 14400 bps Modem */ ++ { "PNPC101", 0 }, ++ /* Standard 28800 bps Modem*/ ++ { "PNPC102", 0 }, ++ /* Standard Modem*/ ++ { "PNPC103", 0 }, ++ /* Standard 9600 bps Modem*/ ++ { "PNPC104", 0 }, ++ /* Standard 14400 bps Modem*/ ++ { "PNPC105", 0 }, ++ /* Standard 28800 bps Modem*/ ++ { "PNPC106", 0 }, ++ /* Standard Modem */ ++ { "PNPC107", 0 }, ++ /* Standard 9600 bps Modem */ ++ { "PNPC108", 0 }, ++ /* Standard 14400 bps Modem */ ++ { "PNPC109", 0 }, ++ /* Standard 28800 bps Modem */ ++ { "PNPC10A", 0 }, ++ /* Standard Modem */ ++ { "PNPC10B", 0 }, ++ /* Standard 9600 bps Modem */ ++ { "PNPC10C", 0 }, ++ /* Standard 14400 bps Modem */ ++ { "PNPC10D", 0 }, ++ /* Standard 28800 bps Modem */ ++ { "PNPC10E", 0 }, ++ /* Standard Modem */ ++ { "PNPC10F", 0 }, ++ /* Standard PCMCIA Card Modem */ ++ { "PNP2000", 0 }, ++ /* Rockwell */ ++ /* Modular Technology */ ++ /* Rockwell 33.6 DPF Internal PnP */ ++ /* Modular Technology 33.6 Internal PnP */ ++ { "ROK0030", 0 }, ++ /* Kortex International */ ++ /* KORTEX 14400 Externe PnP */ ++ { "ROK0100", 0 }, ++ /* Viking Components, Inc */ ++ /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ ++ { "ROK4920", 0 }, ++ /* Rockwell */ ++ /* British Telecom */ ++ /* Modular Technology */ ++ /* Rockwell 33.6 DPF External PnP */ ++ /* BT Prologue 33.6 External PnP */ ++ /* Modular Technology 33.6 External PnP */ ++ { "RSS00A0", 0 }, ++ /* Viking 56K FAX INT */ ++ { "RSS0262", 0 }, ++ /* SupraExpress 28.8 Data/Fax PnP modem */ ++ { "SUP1310", 0 }, ++ /* SupraExpress 33.6 Data/Fax PnP modem */ ++ { "SUP1421", 0 }, ++ /* SupraExpress 33.6 Data/Fax PnP modem */ ++ { "SUP1590", 0 }, ++ /* SupraExpress 33.6 Data/Fax PnP modem */ ++ { "SUP1760", 0 }, ++ /* Phoebe Micro */ ++ /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ ++ { "TEX0011", 0 }, ++ /* Archtek America Corp. */ ++ /* Archtek SmartLink Modem 3334BT Plug & Play */ ++ { "UAC000F", 0 }, ++ /* 3Com Corp. */ ++ /* Gateway Telepath IIvi 33.6 */ ++ { "USR0000", 0 }, ++ /* Sportster Vi 14.4 PnP FAX Voicemail */ ++ { "USR0004", 0 }, ++ /* U.S. Robotics 33.6K Voice INT PnP */ ++ { "USR0006", 0 }, ++ /* U.S. Robotics 33.6K Voice EXT PnP */ ++ { "USR0007", 0 }, ++ /* U.S. Robotics 33.6K Voice INT PnP */ ++ { "USR2002", 0 }, ++ /* U.S. Robotics 56K Voice INT PnP */ ++ { "USR2070", 0 }, ++ /* U.S. Robotics 56K Voice EXT PnP */ ++ { "USR2080", 0 }, ++ /* U.S. Robotics 56K FAX INT */ ++ { "USR3031", 0 }, ++ /* U.S. Robotics 56K Voice INT PnP */ ++ { "USR3070", 0 }, ++ /* U.S. Robotics 56K Voice EXT PnP */ ++ { "USR3080", 0 }, ++ /* U.S. Robotics 56K Voice INT PnP */ ++ { "USR3090", 0 }, ++ /* U.S. Robotics 56K Message */ ++ { "USR9100", 0 }, ++ /* U.S. Robotics 56K FAX EXT PnP*/ ++ { "USR9160", 0 }, ++ /* U.S. Robotics 56K FAX INT PnP*/ ++ { "USR9170", 0 }, ++ /* U.S. Robotics 56K Voice EXT PnP*/ ++ { "USR9180", 0 }, ++ /* U.S. Robotics 56K Voice INT PnP*/ ++ { "USR9190", 0 }, ++ { "", 0 } ++}; ++ ++static void inline avoid_irq_share(struct pci_dev *dev) ++{ ++ int i, map = 0x1FF8; ++ struct serial_state *state = rs_table; ++ struct isapnp_irq *irq; ++ struct isapnp_resources *res = dev->sysdata; ++ ++ for (i = 0; i < NR_PORTS; i++) { ++ if (state->type != PORT_UNKNOWN) ++ clear_bit(state->irq, &map); ++ state++; ++ } ++ ++ for ( ; res; res = res->alt) ++ for(irq = res->irq; irq; irq = irq->next) ++ irq->map = map; ++} ++ ++static char *modem_names[] __devinitdata = { ++ "MODEM", "Modem", "modem", "FAX", "Fax", "fax", ++ "56K", "56k", "K56", "33.6", "28.8", "14.4", ++ "33,600", "28,800", "14,400", "33.600", "28.800", "14.400", ++ "33600", "28800", "14400", "V.90", "V.34", "V.32", 0 ++}; ++ ++static int __devinit check_name(char *name) ++{ ++ char **tmp; ++ ++ for (tmp = modem_names; *tmp; tmp++) ++ if (strstr(name, *tmp)) ++ return 1; ++ ++ return 0; ++} ++ ++static int inline check_compatible_id(struct pci_dev *dev) ++{ ++ int i; ++ for (i = 0; i < DEVICE_COUNT_COMPATIBLE; i++) ++ if ((dev->vendor_compatible[i] == ++ ISAPNP_VENDOR('P', 'N', 'P')) && ++ (swab16(dev->device_compatible[i]) >= 0xc000) && ++ (swab16(dev->device_compatible[i]) <= 0xdfff)) ++ return 0; ++ return 1; ++} ++ ++/* ++ * Given a complete unknown ISA PnP device, try to use some heuristics to ++ * detect modems. Currently use such heuristic set: ++ * - dev->name or dev->bus->name must contain "modem" substring; ++ * - device must have only one IO region (8 byte long) with base adress ++ * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. ++ * ++ * Such detection looks very ugly, but can detect at least some of numerous ++ * ISA PnP modems, alternatively we must hardcode all modems in pnp_devices[] ++ * table. ++ */ ++static int serial_pnp_guess_board(struct pci_dev *dev, int *flags) ++{ ++ struct isapnp_resources *res = (struct isapnp_resources *)dev->sysdata; ++ struct isapnp_resources *resa; ++ ++ if (!(check_name(dev->name) || check_name(dev->bus->name)) && ++ !(check_compatible_id(dev))) ++ return -ENODEV; ++ ++ if (!res || res->next) ++ return -ENODEV; ++ ++ for (resa = res->alt; resa; resa = resa->alt) { ++ struct isapnp_port *port; ++ for (port = res->port; port; port = port->next) ++ if ((port->size == 8) && ++ ((port->min == 0x2f8) || ++ (port->min == 0x3f8) || ++ (port->min == 0x2e8) || ++ (port->min == 0x3e8))) ++ return 0; ++ } ++ ++ return -ENODEV; ++} ++ ++static int ++pnp_init_one(struct pci_dev *dev, const struct pnpbios_device_id *ent, ++ char *slot_name) ++{ ++ struct serial_struct serial_req; ++ int ret, line, flags = ent ? ent->driver_data : 0; ++ ++ if (!ent) { ++ ret = serial_pnp_guess_board(dev, &flags); ++ if (ret) ++ return ret; ++ } ++ ++ if (dev->prepare(dev) < 0) { ++ printk("serial: PNP device '%s' prepare failed\n", ++ slot_name); ++ return -ENODEV; ++ } ++ ++ if (dev->active) ++ return -ENODEV; ++ ++ if (flags & SPCI_FL_NO_SHIRQ) ++ avoid_irq_share(dev); ++ ++ if (dev->activate(dev) < 0) { ++ printk("serial: PNP device '%s' activate failed\n", ++ slot_name); ++ return -ENODEV; ++ } ++ ++ memset(&serial_req, 0, sizeof(serial_req)); ++ serial_req.irq = dev->irq_resource[0].start; ++ serial_req.port = pci_resource_start(dev, 0); ++ if (HIGH_BITS_OFFSET) ++ serial_req.port = pci_resource_start(dev, 0) >> HIGH_BITS_OFFSET; ++ ++#ifdef SERIAL_DEBUG_PCI ++ printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", ++ serial_req.port, serial_req.irq, serial_req.io_type); ++#endif ++ ++ serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; ++ serial_req.baud_base = 115200; ++ line = register_serial(&serial_req); ++ ++ if (line >= 0) { ++ pci_set_drvdata(dev, (void *)(line + 1)); ++ ++ /* ++ * Public health warning: remove this once the 2.5 ++ * pnpbios_module_init() stuff is incorporated. ++ */ ++ dev->driver = (void *)pnp_dev_table; ++ } else ++ dev->deactivate(dev); ++ ++ return line >= 0 ? 0 : -ENODEV; ++} ++ ++static void pnp_remove_one(struct pci_dev *dev) ++{ ++ int line = (int)pci_get_drvdata(dev); ++ ++ if (line) { ++ pci_set_drvdata(dev, NULL); ++ ++ unregister_serial(line - 1); ++ ++ dev->deactivate(dev); ++ } ++} ++ ++static char hex[] = "0123456789ABCDEF"; ++ ++/* ++ * This function should vanish when 2.5 comes around and ++ * we have pnpbios_module_init() ++ */ ++static void pnp_init(void) ++{ ++ const struct pnpbios_device_id *id; ++ struct pci_dev *dev = NULL; ++ ++#ifdef SERIAL_DEBUG_PNP ++ printk("Entered probe_serial_pnp()\n"); ++#endif ++ ++ isapnp_for_each_dev(dev) { ++ char slot_name[8]; ++ u32 pnpid; ++ ++ if (dev->active) ++ continue; ++ ++ pnpid = dev->vendor << 16 | dev->device; ++ pnpid = cpu_to_le32(pnpid); ++ ++#define HEX(id,a) hex[((id)>>a) & 15] ++#define CHAR(id,a) (0x40 + (((id)>>a) & 31)) ++ slot_name[0] = CHAR(pnpid, 26); ++ slot_name[1] = CHAR(pnpid, 21); ++ slot_name[2] = CHAR(pnpid, 16); ++ slot_name[3] = HEX(pnpid, 12); ++ slot_name[4] = HEX(pnpid, 8); ++ slot_name[5] = HEX(pnpid, 4); ++ slot_name[6] = HEX(pnpid, 0); ++ slot_name[7] = '\0'; ++ ++ for (id = pnp_dev_table; id->id[0]; id++) ++ if (memcmp(id->id, slot_name, 7) == 0) ++ break; ++ ++ if (id->id[0]) ++ pnp_init_one(dev, id, slot_name); ++ else ++ pnp_init_one(dev, NULL, slot_name); ++ } ++ ++#ifdef SERIAL_DEBUG_PNP ++ printk("Leaving probe_serial_pnp() (probe finished)\n"); ++#endif ++} ++ ++static int __init serial8250_pnp_init(void) ++{ ++ if (!isapnp_present()) { ++#ifdef SERIAL_DEBUG_PNP ++ printk("Leaving probe_serial_pnp() (no isapnp)\n"); ++#endif ++ return -ENODEV; ++ } ++ pnp_init(); ++ return 0; ++} ++ ++static void __exit serial8250_pnp_exit(void) ++{ ++ struct pci_dev *dev = NULL; ++ ++ isapnp_for_each_dev(dev) { ++ if (dev->driver != (void *)pnp_dev_table) ++ continue; ++ pnp_remove_one(dev); ++ } ++} ++ ++module_init(serial8250_pnp_init); ++module_exit(serial8250_pnp_exit); ++ ++EXPORT_NO_SYMBOLS; ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Generic 8250/16x50 PNPBIOS serial probe module"); ++MODULE_GENERIC_TABLE(pnp, pnp_dev_table); ++ +diff -urN linux-2.4.26/drivers/serial/Config.in linux-2.4.26-vrs1/drivers/serial/Config.in +--- linux-2.4.26/drivers/serial/Config.in 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/Config.in 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,91 @@ ++# ++# Serial device configuration ++# ++# $Id: Config.in,v 1.4 2001/10/12 15:46:58 rmk Exp $ ++# ++mainmenu_option next_comment ++comment 'Serial drivers' ++ ++if [ "$CONFIG_ARM" = "y" ]; then ++ # I don't have this in my tree yet. ++ dep_bool 'Anakin serial port support' CONFIG_SERIAL_ANAKIN $CONFIG_ARCH_ANAKIN ++ dep_bool ' Console on Anakin serial port' CONFIG_SERIAL_ANAKIN_CONSOLE $CONFIG_SERIAL_ANAKIN ++ if [ "$CONFIG_SERIAL_ANAKIN" = "y" ]; then ++ int ' Default Anakin serial baudrate' CONFIG_ANAKIN_DEFAULT_BAUDRATE 9600 ++ fi ++ ++ dep_tristate 'ARM AMBA serial port support' CONFIG_SERIAL_AMBA $CONFIG_ARCH_INTEGRATOR ++ dep_bool ' Support for console on AMBA serial port' CONFIG_SERIAL_AMBA_CONSOLE $CONFIG_SERIAL_AMBA ++ if [ "$CONFIG_SERIAL_AMBA" = "y" ]; then ++ define_bool CONFIG_SERIAL_INTEGRATOR y ++ fi ++ ++ dep_tristate 'CLPS711X serial port support' CONFIG_SERIAL_CLPS711X $CONFIG_ARCH_CLPS711X ++ dep_bool ' Support for console on CLPS711X serial port' CONFIG_SERIAL_CLPS711X_CONSOLE $CONFIG_SERIAL_CLPS711X ++ ++ dep_bool 'DC21285 serial port support' CONFIG_SERIAL_21285 $CONFIG_FOOTBRIDGE ++ dep_bool ' Use /dev/ttyS0 device (OBSOLETE)' CONFIG_SERIAL_21285_OLD $CONFIG_SERIAL_21285 $CONFIG_OBSOLETE ++ dep_bool ' Console on DC21285 serial port' CONFIG_SERIAL_21285_CONSOLE $CONFIG_SERIAL_21285 ++ ++ dep_bool 'Excalibur serial port (uart00) support' CONFIG_SERIAL_UART00 $CONFIG_ARCH_CAMELOT ++ dep_bool ' Support for console on Excalibur serial port' CONFIG_SERIAL_UART00_CONSOLE $CONFIG_SERIAL_UART00 ++ ++ ++ dep_bool 'SA1100 serial port support' CONFIG_SERIAL_SA1100 $CONFIG_ARCH_SA1100 ++ dep_bool ' Console on SA1100 serial port' CONFIG_SERIAL_SA1100_CONSOLE $CONFIG_SERIAL_SA1100 ++ if [ "$CONFIG_SERIAL_SA1100" = "y" ]; then ++ int ' Default SA1100 serial baudrate' CONFIG_SA1100_DEFAULT_BAUDRATE 9600 ++ fi ++ ++ dep_tristate 'ARM Omaha serial port support' CONFIG_SERIAL_OMAHA $CONFIG_ARCH_OMAHA ++ dep_bool ' Support for console on Omaha serial port' CONFIG_SERIAL_OMAHA_CONSOLE $CONFIG_SERIAL_OMAHA ++ ++ dep_tristate 'AT91RM9200 serial port support' CONFIG_SERIAL_AT91 $CONFIG_ARCH_AT91RM9200 ++ dep_bool ' Console on AT91RM9200 serial port' CONFIG_SERIAL_AT91_CONSOLE $CONFIG_SERIAL_AT91 ++ ++fi ++# ++# The new 8250/16550 serial drivers ++dep_tristate '8250/16550 and compatible serial support (EXPERIMENTAL)' CONFIG_SERIAL_8250 $CONFIG_EXPERIMENTAL ++dep_bool ' Console on 8250/16550 and compatible serial port (EXPERIMENTAL)' CONFIG_SERIAL_8250_CONSOLE $CONFIG_SERIAL_8250 $CONFIG_EXPERIMENTAL ++ ++dep_mbool 'Extended 8250/16550 serial driver options' CONFIG_SERIAL_8250_EXTENDED $CONFIG_SERIAL_8250 ++dep_bool ' Support more than 4 serial ports' CONFIG_SERIAL_8250_MANY_PORTS $CONFIG_SERIAL_8250_EXTENDED ++dep_bool ' Support for sharing serial interrupts' CONFIG_SERIAL_8250_SHARE_IRQ $CONFIG_SERIAL_8250_EXTENDED ++dep_bool ' Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_8250_DETECT_IRQ $CONFIG_SERIAL_8250_EXTENDED ++dep_bool ' Support special multiport boards' CONFIG_SERIAL_8250_MULTIPORT $CONFIG_SERIAL_8250_EXTENDED ++dep_bool ' Support Bell Technologies HUB6 card' CONFIG_SERIAL_8250_HUB6 $CONFIG_SERIAL_8250_EXTENDED ++ ++if [ "$CONFIG_SERIAL_AMBA" = "y" -o \ ++ "$CONFIG_SERIAL_CLPS711X" = "y" -o \ ++ "$CONFIG_SERIAL_SA1100" = "y" -o \ ++ "$CONFIG_SERIAL_ANAKIN" = "y" -o \ ++ "$CONFIG_SERIAL_UART00" = "y" -o \ ++ "$CONFIG_SERIAL_8250" = "y" -o \ ++ "$CONFIG_SERIAL_OMAHA" = "y" -o \ ++ "$CONFIG_SERIAL_AT91" = "y" ]; then ++ define_bool CONFIG_SERIAL_CORE y ++else ++ if [ "$CONFIG_SERIAL_AMBA" = "m" -o \ ++ "$CONFIG_SERIAL_CLPS711X" = "m" -o \ ++ "$CONFIG_SERIAL_SA1100" = "m" -o \ ++ "$CONFIG_SERIAL_ANAKIN" = "m" -o \ ++ "$CONFIG_SERIAL_UART00" = "m" -o \ ++ "$CONFIG_SERIAL_8250" = "m" -o \ ++ "$CONFIG_SERIAL_OMAHA" = "m" -o \ ++ "$CONFIG_SERIAL_AT91" = "m" ]; then ++ define_bool CONFIG_SERIAL_CORE m ++ fi ++fi ++if [ "$CONFIG_SERIAL_AMBA_CONSOLE" = "y" -o \ ++ "$CONFIG_SERIAL_CLPS711X_CONSOLE" = "y" -o \ ++ "$CONFIG_SERIAL_SA1100_CONSOLE" = "y" -o \ ++ "$CONFIG_SERIAL_ANAKIN_CONSOLE" = "y" -o \ ++ "$CONFIG_SERIAL_UART00_CONSOLE" = "y" -o \ ++ "$CONFIG_SERIAL_8250_CONSOLE" = "y" -o \ ++ "$CONFIG_SERIAL_OMAHA" = "y" -o \ ++ "$CONFIG_SERIAL_AT91_CONSOLE" = "y" ]; then ++ define_bool CONFIG_SERIAL_CORE_CONSOLE y ++fi ++ ++endmenu +diff -urN linux-2.4.26/drivers/serial/Makefile linux-2.4.26-vrs1/drivers/serial/Makefile +--- linux-2.4.26/drivers/serial/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/Makefile 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,39 @@ ++# ++# Makefile for the kernel serial device drivers. ++# ++# Note! Dependencies are done automagically by 'make dep', which also ++# removes any old dependencies. DON'T put your own dependencies here ++# unless it's something special (ie not a .c file). ++# ++# Note 2! The CFLAGS definitions are now inherited from the ++# parent makes.. ++# ++# $Id: Makefile,v 1.2 2001/10/12 15:46:58 rmk Exp $ ++# ++ ++O_TARGET := serial.o ++ ++export-objs := core.o 8250.o ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++serial-8250-y := ++serial-8250-$(CONFIG_PCI) += 8250_pci.o ++serial-8250-$(CONFIG_ISAPNP) += 8250_pnp.o ++obj-$(CONFIG_SERIAL_CORE) += core.o ++obj-$(CONFIG_SERIAL_21285) += 21285.o ++obj-$(CONFIG_SERIAL_8250) += 8250.o $(serial-8250-y) ++obj-$(CONFIG_SERIAL_ANAKIN) += anakin.o ++obj-$(CONFIG_SERIAL_AMBA) += amba.o ++obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o ++obj-$(CONFIG_SERIAL_SA1100) += sa1100.o ++obj-$(CONFIG_SERIAL_UART00) += uart00.o ++obj-$(CONFIG_SERIAL_OMAHA) += omaha.o ++obj-$(CONFIG_SERIAL_AT91US3) += at91us3.o ++ ++include $(TOPDIR)/Rules.make ++ ++fastdep: ++ +diff -urN linux-2.4.26/drivers/serial/amba.c linux-2.4.26-vrs1/drivers/serial/amba.c +--- linux-2.4.26/drivers/serial/amba.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/amba.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,770 @@ ++/* ++ * linux/drivers/char/serial_amba.c ++ * ++ * Driver for AMBA serial ports ++ * ++ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. ++ * ++ * Copyright 1999 ARM Limited ++ * Copyright (C) 2000 Deep Blue Solutions Ltd. ++ * ++ * 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 ++ * ++ * $Id: amba.c,v 1.9.2.2 2002/10/24 09:53:25 rmk Exp $ ++ * ++ * This is a generic driver for ARM AMBA-type serial ports. They ++ * have a lot of 16550-like features, but are not register compatable. ++ * Note that although they do have CTS, DCD and DSR inputs, they do ++ * not have an RI input, nor do they have DTR or RTS outputs. If ++ * required, these have to be supplied via some other means (eg, GPIO) ++ * and hooked into this driver. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ++#define SUPPORT_SYSRQ ++#endif ++ ++#include ++ ++#include ++ ++#define UART_NR 2 ++ ++#define SERIAL_AMBA_MAJOR 204 ++#define SERIAL_AMBA_MINOR 16 ++#define SERIAL_AMBA_NR UART_NR ++ ++#define CALLOUT_AMBA_NAME "cuaam" ++#define CALLOUT_AMBA_MAJOR 205 ++#define CALLOUT_AMBA_MINOR 16 ++#define CALLOUT_AMBA_NR UART_NR ++ ++static struct tty_driver normal, callout; ++static struct tty_struct *amba_table[UART_NR]; ++static struct termios *amba_termios[UART_NR], *amba_termios_locked[UART_NR]; ++#ifdef SUPPORT_SYSRQ ++static struct console amba_console; ++#endif ++ ++#define AMBA_ISR_PASS_LIMIT 256 ++ ++/* ++ * Access macros for the AMBA UARTs ++ */ ++#define UART_GET_INT_STATUS(p) readb((p)->membase + AMBA_UARTIIR) ++#define UART_PUT_ICR(p, c) writel((c), (p)->membase + AMBA_UARTICR) ++#define UART_GET_FR(p) readb((p)->membase + AMBA_UARTFR) ++#define UART_GET_CHAR(p) readb((p)->membase + AMBA_UARTDR) ++#define UART_PUT_CHAR(p, c) writel((c), (p)->membase + AMBA_UARTDR) ++#define UART_GET_RSR(p) readb((p)->membase + AMBA_UARTRSR) ++#define UART_GET_CR(p) readb((p)->membase + AMBA_UARTCR) ++#define UART_PUT_CR(p,c) writel((c), (p)->membase + AMBA_UARTCR) ++#define UART_GET_LCRL(p) readb((p)->membase + AMBA_UARTLCR_L) ++#define UART_PUT_LCRL(p,c) writel((c), (p)->membase + AMBA_UARTLCR_L) ++#define UART_GET_LCRM(p) readb((p)->membase + AMBA_UARTLCR_M) ++#define UART_PUT_LCRM(p,c) writel((c), (p)->membase + AMBA_UARTLCR_M) ++#define UART_GET_LCRH(p) readb((p)->membase + AMBA_UARTLCR_H) ++#define UART_PUT_LCRH(p,c) writel((c), (p)->membase + AMBA_UARTLCR_H) ++#define UART_RX_DATA(s) (((s) & AMBA_UARTFR_RXFE) == 0) ++#define UART_TX_READY(s) (((s) & AMBA_UARTFR_TXFF) == 0) ++#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0) ++ ++#define UART_DUMMY_RSR_RX 256 ++#define UART_PORT_SIZE 64 ++ ++/* ++ * On the Integrator platform, the port RTS and DTR are provided by ++ * bits in the following SC_CTRLS register bits: ++ * RTS DTR ++ * UART0 7 6 ++ * UART1 5 4 ++ */ ++#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) ++#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) ++ ++/* ++ * We wrap our port structure around the generic uart_port. ++ */ ++struct uart_amba_port { ++ struct uart_port port; ++ unsigned int dtr_mask; ++ unsigned int rts_mask; ++ unsigned int old_status; ++}; ++ ++static void ambauart_stop_tx(struct uart_port *port, unsigned int tty_stop) ++{ ++ unsigned int cr; ++ ++ cr = UART_GET_CR(port); ++ cr &= ~AMBA_UARTCR_TIE; ++ UART_PUT_CR(port, cr); ++} ++ ++static void ambauart_start_tx(struct uart_port *port, unsigned int tty_start) ++{ ++ unsigned int cr; ++ ++ cr = UART_GET_CR(port); ++ cr |= AMBA_UARTCR_TIE; ++ UART_PUT_CR(port, cr); ++} ++ ++static void ambauart_stop_rx(struct uart_port *port) ++{ ++ unsigned int cr; ++ ++ cr = UART_GET_CR(port); ++ cr &= ~(AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE); ++ UART_PUT_CR(port, cr); ++} ++ ++static void ambauart_enable_ms(struct uart_port *port) ++{ ++ unsigned int cr; ++ ++ cr = UART_GET_CR(port); ++ cr |= AMBA_UARTCR_MSIE; ++ UART_PUT_CR(port, cr); ++} ++ ++static void ++#ifdef SUPPORT_SYSRQ ++ambauart_rx_chars(struct uart_port *port, struct pt_regs *regs) ++#else ++ambauart_rx_chars(struct uart_port *port) ++#endif ++{ ++ struct tty_struct *tty = port->info->tty; ++ unsigned int status, ch, rsr, max_count = 256; ++ ++ status = UART_GET_FR(port); ++ while (UART_RX_DATA(status) && max_count--) { ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) { ++ tty->flip.tqueue.routine((void *)tty); ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) { ++ printk(KERN_WARNING "TTY_DONT_FLIP set\n"); ++ return; ++ } ++ } ++ ++ ch = UART_GET_CHAR(port); ++ ++ *tty->flip.char_buf_ptr = ch; ++ *tty->flip.flag_buf_ptr = TTY_NORMAL; ++ port->icount.rx++; ++ ++ /* ++ * Note that the error handling code is ++ * out of the main execution path ++ */ ++ rsr = UART_GET_RSR(port) | UART_DUMMY_RSR_RX; ++ if (rsr & AMBA_UARTRSR_ANY) { ++ if (rsr & AMBA_UARTRSR_BE) { ++ rsr &= ~(AMBA_UARTRSR_FE | AMBA_UARTRSR_PE); ++ port->icount.brk++; ++ if (uart_handle_break(port)) ++ goto ignore_char; ++ } else if (rsr & AMBA_UARTRSR_PE) ++ port->icount.parity++; ++ else if (rsr & AMBA_UARTRSR_FE) ++ port->icount.frame++; ++ if (rsr & AMBA_UARTRSR_OE) ++ port->icount.overrun++; ++ ++ rsr &= port->read_status_mask; ++ ++ if (rsr & AMBA_UARTRSR_BE) ++ *tty->flip.flag_buf_ptr = TTY_BREAK; ++ else if (rsr & AMBA_UARTRSR_PE) ++ *tty->flip.flag_buf_ptr = TTY_PARITY; ++ else if (rsr & AMBA_UARTRSR_FE) ++ *tty->flip.flag_buf_ptr = TTY_FRAME; ++ } ++ ++ if (uart_handle_sysrq_char(port, ch, regs)) ++ goto ignore_char; ++ ++ if ((rsr & port->ignore_status_mask) == 0) { ++ tty->flip.flag_buf_ptr++; ++ tty->flip.char_buf_ptr++; ++ tty->flip.count++; ++ } ++ if ((rsr & AMBA_UARTRSR_OE) && ++ tty->flip.count < TTY_FLIPBUF_SIZE) { ++ /* ++ * Overrun is special, since it's reported ++ * immediately, and doesn't affect the current ++ * character ++ */ ++ *tty->flip.char_buf_ptr++ = 0; ++ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; ++ tty->flip.count++; ++ } ++ ignore_char: ++ status = UART_GET_FR(port); ++ } ++ tty_flip_buffer_push(tty); ++ return; ++} ++ ++static void ambauart_tx_chars(struct uart_port *port) ++{ ++ struct circ_buf *xmit = &port->info->xmit; ++ int count; ++ ++ if (port->x_char) { ++ UART_PUT_CHAR(port, port->x_char); ++ port->icount.tx++; ++ port->x_char = 0; ++ return; ++ } ++ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { ++ ambauart_stop_tx(port, 0); ++ return; ++ } ++ ++ count = port->fifosize >> 1; ++ do { ++ UART_PUT_CHAR(port, xmit->buf[xmit->tail]); ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ port->icount.tx++; ++ if (uart_circ_empty(xmit)) ++ break; ++ } while (--count > 0); ++ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(port); ++ ++ if (uart_circ_empty(xmit)) ++ ambauart_stop_tx(port, 0); ++} ++ ++static void ambauart_modem_status(struct uart_port *port) ++{ ++ struct uart_amba_port *uap = (struct uart_amba_port *)port; ++ unsigned int status, delta; ++ ++ UART_PUT_ICR(&uap->port, 0); ++ ++ status = UART_GET_FR(&uap->port) & AMBA_UARTFR_MODEM_ANY; ++ ++ delta = status ^ uap->old_status; ++ uap->old_status = status; ++ ++ if (!delta) ++ return; ++ ++ if (delta & AMBA_UARTFR_DCD) ++ uart_handle_dcd_change(&uap->port, status & AMBA_UARTFR_DCD); ++ ++ if (delta & AMBA_UARTFR_DSR) ++ uap->port.icount.dsr++; ++ ++ if (delta & AMBA_UARTFR_CTS) ++ uart_handle_cts_change(&uap->port, status & AMBA_UARTFR_CTS); ++ ++ wake_up_interruptible(&uap->port.info->delta_msr_wait); ++} ++ ++static void ambauart_int(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct uart_port *port = dev_id; ++ unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; ++ ++ status = UART_GET_INT_STATUS(port); ++ do { ++ if (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS)) ++#ifdef SUPPORT_SYSRQ ++ ambauart_rx_chars(port, regs); ++#else ++ ambauart_rx_chars(port); ++#endif ++ if (status & AMBA_UARTIIR_TIS) ++ ambauart_tx_chars(port); ++ if (status & AMBA_UARTIIR_MIS) ++ ambauart_modem_status(port); ++ ++ if (pass_counter-- == 0) ++ break; ++ ++ status = UART_GET_INT_STATUS(port); ++ } while (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS | ++ AMBA_UARTIIR_TIS)); ++} ++ ++static unsigned int ambauart_tx_empty(struct uart_port *port) ++{ ++ return UART_GET_FR(port) & AMBA_UARTFR_BUSY ? 0 : TIOCSER_TEMT; ++} ++ ++static unsigned int ambauart_get_mctrl(struct uart_port *port) ++{ ++ unsigned int result = 0; ++ unsigned int status; ++ ++ status = UART_GET_FR(port); ++ if (status & AMBA_UARTFR_DCD) ++ result |= TIOCM_CAR; ++ if (status & AMBA_UARTFR_DSR) ++ result |= TIOCM_DSR; ++ if (status & AMBA_UARTFR_CTS) ++ result |= TIOCM_CTS; ++ ++ return result; ++} ++ ++static void ambauart_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++ struct uart_amba_port *uap = (struct uart_amba_port *)port; ++ unsigned int ctrls = 0, ctrlc = 0; ++ ++ if (mctrl & TIOCM_RTS) ++ ctrlc |= uap->rts_mask; ++ else ++ ctrls |= uap->rts_mask; ++ ++ if (mctrl & TIOCM_DTR) ++ ctrlc |= uap->dtr_mask; ++ else ++ ctrls |= uap->dtr_mask; ++ ++ __raw_writel(ctrls, SC_CTRLS); ++ __raw_writel(ctrlc, SC_CTRLC); ++} ++ ++static void ambauart_break_ctl(struct uart_port *port, int break_state) ++{ ++ unsigned long flags; ++ unsigned int lcr_h; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ lcr_h = UART_GET_LCRH(port); ++ if (break_state == -1) ++ lcr_h |= AMBA_UARTLCR_H_BRK; ++ else ++ lcr_h &= ~AMBA_UARTLCR_H_BRK; ++ UART_PUT_LCRH(port, lcr_h); ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static int ambauart_startup(struct uart_port *port) ++{ ++ struct uart_amba_port *uap = (struct uart_amba_port *)port; ++ int retval; ++ ++ /* ++ * Allocate the IRQ ++ */ ++ retval = request_irq(port->irq, ambauart_int, 0, "amba", port); ++ if (retval) ++ return retval; ++ ++ /* ++ * initialise the old status of the modem signals ++ */ ++ uap->old_status = UART_GET_FR(port) & AMBA_UARTFR_MODEM_ANY; ++ ++ /* ++ * Finally, enable interrupts ++ */ ++ UART_PUT_CR(port, AMBA_UARTCR_UARTEN | AMBA_UARTCR_RIE | ++ AMBA_UARTCR_RTIE); ++ ++ return 0; ++} ++ ++static void ambauart_shutdown(struct uart_port *port) ++{ ++ /* ++ * Free the interrupt ++ */ ++ free_irq(port->irq, port); ++ ++ /* ++ * disable all interrupts, disable the port ++ */ ++ UART_PUT_CR(port, 0); ++ ++ /* disable break condition and fifos */ ++ UART_PUT_LCRH(port, UART_GET_LCRH(port) & ++ ~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN)); ++} ++ ++static void ambauart_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot) ++{ ++ unsigned int lcr_h, old_cr; ++ unsigned long flags; ++ ++#if DEBUG ++ printk("ambauart_set_cflag(0x%x) called\n", cflag); ++#endif ++ /* byte size and parity */ ++ switch (cflag & CSIZE) { ++ case CS5: ++ lcr_h = AMBA_UARTLCR_H_WLEN_5; ++ break; ++ case CS6: ++ lcr_h = AMBA_UARTLCR_H_WLEN_6; ++ break; ++ case CS7: ++ lcr_h = AMBA_UARTLCR_H_WLEN_7; ++ break; ++ default: // CS8 ++ lcr_h = AMBA_UARTLCR_H_WLEN_8; ++ break; ++ } ++ if (cflag & CSTOPB) ++ lcr_h |= AMBA_UARTLCR_H_STP2; ++ if (cflag & PARENB) { ++ lcr_h |= AMBA_UARTLCR_H_PEN; ++ if (!(cflag & PARODD)) ++ lcr_h |= AMBA_UARTLCR_H_EPS; ++ } ++ if (port->fifosize > 1) ++ lcr_h |= AMBA_UARTLCR_H_FEN; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ ++ port->read_status_mask = AMBA_UARTRSR_OE; ++ if (iflag & INPCK) ++ port->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; ++ if (iflag & (BRKINT | PARMRK)) ++ port->read_status_mask |= AMBA_UARTRSR_BE; ++ ++ /* ++ * Characters to ignore ++ */ ++ port->ignore_status_mask = 0; ++ if (iflag & IGNPAR) ++ port->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; ++ if (iflag & IGNBRK) { ++ port->ignore_status_mask |= AMBA_UARTRSR_BE; ++ /* ++ * If we're ignoring parity and break indicators, ++ * ignore overruns too (for real raw support). ++ */ ++ if (iflag & IGNPAR) ++ port->ignore_status_mask |= AMBA_UARTRSR_OE; ++ } ++ ++ /* ++ * Ignore all characters if CREAD is not set. ++ */ ++ if ((cflag & CREAD) == 0) ++ port->ignore_status_mask |= UART_DUMMY_RSR_RX; ++ ++ old_cr = UART_GET_CR(port) & ~AMBA_UARTCR_MSIE; ++ ++ if (UART_ENABLE_MS(port, cflag)) ++ old_cr |= AMBA_UARTCR_MSIE; ++ ++ UART_PUT_CR(port, 0); ++ ++ /* Set baud rate */ ++ quot -= 1; ++ UART_PUT_LCRM(port, ((quot & 0xf00) >> 8)); ++ UART_PUT_LCRL(port, (quot & 0xff)); ++ ++ /* ++ * ----------v----------v----------v----------v----- ++ * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L ++ * ----------^----------^----------^----------^----- ++ */ ++ UART_PUT_LCRH(port, lcr_h); ++ UART_PUT_CR(port, old_cr); ++ ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static const char *ambauart_type(struct uart_port *port) ++{ ++ return port->type == PORT_AMBA ? "AMBA" : NULL; ++} ++ ++/* ++ * Release the memory region(s) being used by 'port' ++ */ ++static void ambauart_release_port(struct uart_port *port) ++{ ++ release_mem_region(port->mapbase, UART_PORT_SIZE); ++} ++ ++/* ++ * Request the memory region(s) being used by 'port' ++ */ ++static int ambauart_request_port(struct uart_port *port) ++{ ++ return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_amba") ++ != NULL ? 0 : -EBUSY; ++} ++ ++/* ++ * Configure/autoconfigure the port. ++ */ ++static void ambauart_config_port(struct uart_port *port, int flags) ++{ ++ if (flags & UART_CONFIG_TYPE) { ++ port->type = PORT_AMBA; ++ ambauart_request_port(port); ++ } ++} ++ ++/* ++ * verify the new serial_struct (for TIOCSSERIAL). ++ */ ++static int ambauart_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ int ret = 0; ++ if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) ++ ret = -EINVAL; ++ if (ser->irq < 0 || ser->irq >= NR_IRQS) ++ ret = -EINVAL; ++ if (ser->baud_base < 9600) ++ ret = -EINVAL; ++ return ret; ++} ++ ++static struct uart_ops amba_pops = { ++ .tx_empty = ambauart_tx_empty, ++ .set_mctrl = ambauart_set_mctrl, ++ .get_mctrl = ambauart_get_mctrl, ++ .stop_tx = ambauart_stop_tx, ++ .start_tx = ambauart_start_tx, ++ .stop_rx = ambauart_stop_rx, ++ .enable_ms = ambauart_enable_ms, ++ .break_ctl = ambauart_break_ctl, ++ .startup = ambauart_startup, ++ .shutdown = ambauart_shutdown, ++ .change_speed = ambauart_change_speed, ++ .type = ambauart_type, ++ .release_port = ambauart_release_port, ++ .request_port = ambauart_request_port, ++ .config_port = ambauart_config_port, ++ .verify_port = ambauart_verify_port, ++}; ++ ++static struct uart_amba_port amba_ports[UART_NR] = { ++ { ++ .port = { ++ .membase = (void *)IO_ADDRESS(INTEGRATOR_UART0_BASE), ++ .mapbase = INTEGRATOR_UART0_BASE, ++ .iotype = SERIAL_IO_MEM, ++ .irq = IRQ_UARTINT0, ++ .uartclk = 14745600, ++ .fifosize = 16, ++ .ops = &amba_pops, ++ .flags = ASYNC_BOOT_AUTOCONF, ++ .line = 0, ++ }, ++ .dtr_mask = 1 << 5, ++ .rts_mask = 1 << 4, ++ }, ++ { ++ .port = { ++ .membase = (void *)IO_ADDRESS(INTEGRATOR_UART1_BASE), ++ .mapbase = INTEGRATOR_UART1_BASE, ++ .iotype = SERIAL_IO_MEM, ++ .irq = IRQ_UARTINT1, ++ .uartclk = 14745600, ++ .fifosize = 16, ++ .ops = &amba_pops, ++ .flags = ASYNC_BOOT_AUTOCONF, ++ .line = 1, ++ }, ++ .dtr_mask = 1 << 7, ++ .rts_mask = 1 << 6, ++ } ++}; ++ ++#ifdef CONFIG_SERIAL_AMBA_CONSOLE ++ ++static void ambauart_console_write(struct console *co, const char *s, unsigned int count) ++{ ++ struct uart_port *port = &amba_ports[co->index].port; ++ unsigned int status, old_cr; ++ int i; ++ ++ /* ++ * First save the CR then disable the interrupts ++ */ ++ old_cr = UART_GET_CR(port); ++ UART_PUT_CR(port, AMBA_UARTCR_UARTEN); ++ ++ /* ++ * Now, do each character ++ */ ++ for (i = 0; i < count; i++) { ++ do { ++ status = UART_GET_FR(port); ++ } while (!UART_TX_READY(status)); ++ UART_PUT_CHAR(port, s[i]); ++ if (s[i] == '\n') { ++ do { ++ status = UART_GET_FR(port); ++ } while (!UART_TX_READY(status)); ++ UART_PUT_CHAR(port, '\r'); ++ } ++ } ++ ++ /* ++ * Finally, wait for transmitter to become empty ++ * and restore the TCR ++ */ ++ do { ++ status = UART_GET_FR(port); ++ } while (status & AMBA_UARTFR_BUSY); ++ UART_PUT_CR(port, old_cr); ++} ++ ++static kdev_t ambauart_console_device(struct console *co) ++{ ++ return MKDEV(SERIAL_AMBA_MAJOR, SERIAL_AMBA_MINOR + co->index); ++} ++ ++static void __init ++ambauart_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) ++{ ++ if (UART_GET_CR(port) & AMBA_UARTCR_UARTEN) { ++ unsigned int lcr_h, quot; ++ lcr_h = UART_GET_LCRH(port); ++ ++ *parity = 'n'; ++ if (lcr_h & AMBA_UARTLCR_H_PEN) { ++ if (lcr_h & AMBA_UARTLCR_H_EPS) ++ *parity = 'e'; ++ else ++ *parity = 'o'; ++ } ++ ++ if ((lcr_h & 0x60) == AMBA_UARTLCR_H_WLEN_7) ++ *bits = 7; ++ else ++ *bits = 8; ++ ++ quot = UART_GET_LCRL(port) | UART_GET_LCRM(port) << 8; ++ *baud = port->uartclk / (16 * (quot + 1)); ++ } ++} ++ ++static int __init ambauart_console_setup(struct console *co, char *options) ++{ ++ struct uart_port *port; ++ int baud = 38400; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++ ++ /* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++ if (co->index >= UART_NR) ++ co->index = 0; ++ port = &amba_ports[co->index].port; ++ ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ else ++ ambauart_console_get_options(port, &baud, &parity, &bits); ++ ++ return uart_set_options(port, co, baud, parity, bits, flow); ++} ++ ++static struct console amba_console = { ++ .name = "ttyAM", ++ .write = ambauart_console_write, ++ .device = ambauart_console_device, ++ .setup = ambauart_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++}; ++ ++void __init ambauart_console_init(void) ++{ ++ register_console(&amba_console); ++} ++ ++#define AMBA_CONSOLE &amba_console ++#else ++#define AMBA_CONSOLE NULL ++#endif ++ ++static struct uart_driver amba_reg = { ++ .owner = THIS_MODULE, ++ .normal_major = SERIAL_AMBA_MAJOR, ++#ifdef CONFIG_DEVFS_FS ++ .normal_name = "ttyAM%d", ++ .callout_name = "cuaam%d", ++#else ++ .normal_name = "ttyAM", ++ .callout_name = "cuaam", ++#endif ++ .normal_driver = &normal, ++ .callout_major = CALLOUT_AMBA_MAJOR, ++ .callout_driver = &callout, ++ .table = amba_table, ++ .termios = amba_termios, ++ .termios_locked = amba_termios_locked, ++ .minor = SERIAL_AMBA_MINOR, ++ .nr = UART_NR, ++ .cons = AMBA_CONSOLE, ++}; ++ ++static int __init ambauart_init(void) ++{ ++ int ret; ++ ++ ret = uart_register_driver(&amba_reg); ++ if (ret == 0) { ++ int i; ++ ++ for (i = 0; i < UART_NR; i++) ++ uart_add_one_port(&amba_reg, &amba_ports[i].port); ++ } ++ return ret; ++} ++ ++static void __exit ambauart_exit(void) ++{ ++ int i; ++ ++ for (i = 0; i < UART_NR; i++) ++ uart_remove_one_port(&amba_reg, &amba_ports[i].port); ++ ++ uart_unregister_driver(&amba_reg); ++} ++ ++module_init(ambauart_init); ++module_exit(ambauart_exit); ++ ++EXPORT_NO_SYMBOLS; ++ ++MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd"); ++MODULE_DESCRIPTION("ARM AMBA serial port driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/serial/anakin.c linux-2.4.26-vrs1/drivers/serial/anakin.c +--- linux-2.4.26/drivers/serial/anakin.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/anakin.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,545 @@ ++/* ++ * linux/drivers/char/serial_anakin.c ++ * ++ * Based on driver for AMBA serial ports, by ARM Limited, ++ * Deep Blue Solutions Ltd., Linus Torvalds and Theodore Ts'o. ++ * ++ * Copyright (C) 2001 Aleph One Ltd. for Acunia N.V. ++ * ++ * Copyright (C) 2001 Blue Mug, Inc. for Acunia N.V. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Changelog: ++ * 20-Apr-2001 TTC Created ++ * 05-May-2001 W/TTC Updated for serial_core.c ++ * 27-Jun-2001 jonm Minor changes; add mctrl support, switch to ++ * SA_INTERRUPT. Works reliably now. No longer requires ++ * changes to the serial_core API. ++ * ++ * $Id: anakin.c,v 1.5.2.2 2002/10/24 09:53:25 rmk Exp $ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#define UART_NR 5 ++ ++#define SERIAL_ANAKIN_NAME "ttyAN" ++#define SERIAL_ANAKIN_MAJOR 204 ++#define SERIAL_ANAKIN_MINOR 32 ++ ++#define CALLOUT_ANAKIN_NAME "cuaan" ++#define CALLOUT_ANAKIN_MAJOR 205 ++#define CALLOUT_ANAKIN_MINOR 32 ++ ++static struct tty_driver normal, callout; ++static struct tty_struct *anakin_table[UART_NR]; ++static struct termios *anakin_termios[UART_NR], *anakin_termios_locked[UART_NR]; ++static struct uart_state anakin_state[UART_NR]; ++static u_int txenable[NR_IRQS]; /* Software interrupt register */ ++ ++static inline unsigned int ++anakin_in(struct uart_port *port, u_int offset) ++{ ++ return __raw_readl(port->base + offset); ++} ++ ++static inline void ++anakin_out(struct uart_port *port, u_int offset, unsigned int value) ++{ ++ __raw_writel(value, port->base + offset); ++} ++ ++static void ++anakin_stop_tx(struct uart_port *port, u_int from_tty) ++{ ++ txenable[port->irq] = 0; ++} ++ ++static inline void ++anakin_transmit_buffer(struct uart_info *info) ++{ ++ struct uart_port *port = info->port; ++ ++ while (!(anakin_in(port, 0x10) & TXEMPTY)); ++ anakin_out(port, 0x14, info->xmit.buf[info->xmit.tail]); ++ anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST); ++ info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE-1); ++ info->state->icount.tx++; ++ ++ if (info->xmit.head == info->xmit.tail) ++ anakin_stop_tx(port, 0); ++} ++ ++static inline void ++anakin_transmit_x_char(struct uart_info *info) ++{ ++ struct uart_port *port = info->port; ++ ++ anakin_out(port, 0x14, info->x_char); ++ anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST); ++ info->state->icount.tx++; ++ info->x_char = 0; ++} ++ ++static void ++anakin_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty) ++{ ++ unsigned int flags; ++ ++ save_flags_cli(flags); ++ ++ // is it this... or below: if (nonempty ++ if (!txenable[port->irq]) { ++ txenable[port->irq] = TXENABLE; ++ ++ if ((anakin_in(port, 0x10) & TXEMPTY) && nonempty) { ++ anakin_transmit_buffer((struct uart_info*)port->unused); ++ } ++ } ++ ++ restore_flags(flags); ++} ++ ++static void ++anakin_stop_rx(struct uart_port *port) ++{ ++ unsigned long flags; ++ ++ save_flags_cli(flags); ++ while (anakin_in(port, 0x10) & RXRELEASE) ++ anakin_in(port, 0x14); ++ anakin_out(port, 0x18, anakin_in(port, 0x18) | BLOCKRX); ++ restore_flags(flags); ++} ++ ++static void ++anakin_enable_ms(struct uart_port *port) ++{ ++} ++ ++static inline void ++anakin_rx_chars(struct uart_info *info) ++{ ++ unsigned int ch; ++ struct tty_struct *tty = info->tty; ++ ++ if (!(anakin_in(info->port, 0x10) & RXRELEASE)) ++ return; ++ ++ ch = anakin_in(info->port, 0x14) & 0xff; ++ ++ if (tty->flip.count < TTY_FLIPBUF_SIZE) { ++ *tty->flip.char_buf_ptr++ = ch; ++ *tty->flip.flag_buf_ptr++ = TTY_NORMAL; ++ info->state->icount.rx++; ++ tty->flip.count++; ++ } ++ tty_flip_buffer_push(tty); ++} ++ ++static inline void ++anakin_overrun_chars(struct uart_info *info) ++{ ++ unsigned int ch; ++ ++ ch = anakin_in(info->port, 0x14); ++ info->state->icount.overrun++; ++} ++ ++static inline void ++anakin_tx_chars(struct uart_info *info) ++{ ++ if (info->x_char) { ++ anakin_transmit_x_char(info); ++ return; ++ } ++ ++ if (info->xmit.head == info->xmit.tail ++ || info->tty->stopped ++ || info->tty->hw_stopped) { ++ anakin_stop_tx(info->port, 0); ++ return; ++ } ++ ++ anakin_transmit_buffer(info); ++ ++ if (CIRC_CNT(info->xmit.head, ++ info->xmit.tail, ++ UART_XMIT_SIZE) < WAKEUP_CHARS) ++ uart_event(info, EVT_WRITE_WAKEUP); ++} ++ ++static void ++anakin_int(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ unsigned int status; ++ struct uart_info *info = dev_id; ++ ++ status = anakin_in(info->port, 0x1c); ++ ++ if (status & RX) ++ anakin_rx_chars(info); ++ ++ if (status & OVERRUN) ++ anakin_overrun_chars(info); ++ ++ if (txenable[info->port->irq] && (status & TX)) ++ anakin_tx_chars(info); ++} ++ ++static u_int ++anakin_tx_empty(struct uart_port *port) ++{ ++ return anakin_in(port, 0x10) & TXEMPTY ? TIOCSER_TEMT : 0; ++} ++ ++static u_int ++anakin_get_mctrl(struct uart_port *port) ++{ ++ unsigned int status = 0; ++ ++ status |= (anakin_in(port, 0x10) & CTS ? TIOCM_CTS : 0); ++ status |= (anakin_in(port, 0x18) & DCD ? TIOCM_CAR : 0); ++ status |= (anakin_in(port, 0x18) & DTR ? TIOCM_DTR : 0); ++ status |= (anakin_in(port, 0x18) & RTS ? TIOCM_RTS : 0); ++ ++ return status; ++} ++ ++static void ++anakin_set_mctrl(struct uart_port *port, u_int mctrl) ++{ ++ unsigned int status; ++ ++ status = anakin_in(port, 0x18); ++ ++ if (mctrl & TIOCM_RTS) ++ status |= RTS; ++ else ++ status &= ~RTS; ++ ++ if (mctrl & TIOCM_CAR) ++ status |= DCD; ++ else ++ status &= ~DCD; ++ ++ anakin_out(port, 0x18, status); ++} ++ ++static void ++anakin_break_ctl(struct uart_port *port, int break_state) ++{ ++ unsigned int status; ++ ++ status = anakin_in(port, 0x20); ++ ++ if (break_state == -1) ++ status |= SETBREAK; ++ else ++ status &= ~SETBREAK; ++ ++ anakin_out(port, 0x20, status); ++} ++ ++static int ++anakin_startup(struct uart_port *port, struct uart_info *info) ++{ ++ int retval; ++ unsigned int read,write; ++ ++ /* ++ * Allocate the IRQ ++ */ ++ retval = request_irq(port->irq, anakin_int, SA_INTERRUPT, "serial_anakin", info); ++ if (retval) ++ return retval; ++ ++ port->ops->set_mctrl(port, info->mctrl); ++ ++ /* ++ * initialise the old status of the modem signals ++ */ ++ port->old_status = 0; ++ ++ /* ++ * Finally, disable IRQ and softIRQs for first byte) ++ */ ++ txenable[port->irq] = 0; ++ read = anakin_in(port, 0x18); ++ write = (read & ~(RTS | DTR | BLOCKRX)) | IRQENABLE; ++ anakin_out(port, 0x18, write); ++ ++ /* Store the uart_info pointer so we can reference it in ++ * anakin_start_tx() */ ++ port->unused = (u_int)info; ++ ++ return 0; ++} ++ ++static void ++anakin_shutdown(struct uart_port *port, struct uart_info *info) ++{ ++ /* ++ * Free the interrupt ++ */ ++ free_irq(port->irq, info); ++ ++ /* ++ * disable all interrupts, disable the port ++ */ ++ anakin_out(port, 0x18, anakin_in(port, 0x18) & ~IRQENABLE); ++} ++ ++static void ++anakin_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot) ++{ ++ unsigned int flags; ++ ++ save_flags_cli(flags); ++ while (!(anakin_in(port, 0x10) & TXEMPTY)); ++ anakin_out(port, 0x10, (anakin_in(port, 0x10) & ~PRESCALER) ++ | (quot << 3)); ++ ++ //parity always set to none ++ anakin_out(port, 0x18, anakin_in(port, 0x18) & ~PARITY); ++ restore_flags(flags); ++} ++ ++static const char *anakin_type(struct port *port) ++{ ++ return port->type == PORT_ANAKIN ? "ANAKIN" : NULL; ++} ++ ++static struct uart_ops anakin_pops = { ++ tx_empty: anakin_tx_empty, ++ set_mctrl: anakin_set_mctrl, ++ get_mctrl: anakin_get_mctrl, ++ stop_tx: anakin_stop_tx, ++ start_tx: anakin_start_tx, ++ stop_rx: anakin_stop_rx, ++ enable_ms: anakin_enable_ms, ++ break_ctl: anakin_break_ctl, ++ startup: anakin_startup, ++ shutdown: anakin_shutdown, ++ change_speed: anakin_change_speed, ++ type: anakin_type, ++}; ++ ++static struct uart_port anakin_ports[UART_NR] = { ++ { ++ base: IO_BASE + UART0, ++ irq: IRQ_UART0, ++ uartclk: 3686400, ++ fifosize: 0, ++ ops: &anakin_pops, ++ }, ++ { ++ base: IO_BASE + UART1, ++ irq: IRQ_UART1, ++ uartclk: 3686400, ++ fifosize: 0, ++ ops: &anakin_pops, ++ }, ++ { ++ base: IO_BASE + UART2, ++ irq: IRQ_UART2, ++ uartclk: 3686400, ++ fifosize: 0, ++ ops: &anakin_pops, ++ }, ++ { ++ base: IO_BASE + UART3, ++ irq: IRQ_UART3, ++ uartclk: 3686400, ++ fifosize: 0, ++ ops: &anakin_pops, ++ }, ++ { ++ base: IO_BASE + UART4, ++ irq: IRQ_UART4, ++ uartclk: 3686400, ++ fifosize: 0, ++ ops: &anakin_pops, ++ }, ++}; ++ ++ ++#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE ++ ++static void ++anakin_console_write(struct console *co, const char *s, u_int count) ++{ ++ struct uart_port *port = anakin_ports + co->index; ++ unsigned int flags, status, i; ++ ++ /* ++ * First save the status then disable the interrupts ++ */ ++ save_flags_cli(flags); ++ status = anakin_in(port, 0x18); ++ anakin_out(port, 0x18, status & ~IRQENABLE); ++ restore_flags(flags); ++ ++ /* ++ * Now, do each character ++ */ ++ for (i = 0; i < count; i++, s++) { ++ while (!(anakin_in(port, 0x10) & TXEMPTY)); ++ ++ /* ++ * Send the character out. ++ * If a LF, also do CR... ++ */ ++ anakin_out(port, 0x14, *s); ++ anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST); ++ ++ if (*s == 10) { ++ while (!(anakin_in(port, 0x10) & TXEMPTY)); ++ anakin_out(port, 0x14, 13); ++ anakin_out(port, 0x18, anakin_in(port, 0x18) ++ | SENDREQUEST); ++ } ++ } ++ ++ /* ++ * Finally, wait for transmitter to become empty ++ * and restore the interrupts ++ */ ++ while (!(anakin_in(port, 0x10) & TXEMPTY)); ++ ++ if (status & IRQENABLE) ++ save_flags_cli(flags); ++ anakin_out(port, 0x18, anakin_in(port, 0x18) | IRQENABLE); ++ restore_flags(flags); ++} ++ ++static kdev_t ++anakin_console_device(struct console *co) ++{ ++ return MKDEV(SERIAL_ANAKIN_MAJOR, SERIAL_ANAKIN_MINOR + co->index); ++} ++ ++/* ++ * Read the current UART setup. ++ */ ++static void __init ++anakin_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) ++{ ++ int paritycode; ++ ++ *baud = GETBAUD (anakin_in(port, 0x10) & PRESCALER); ++ paritycode = GETPARITY(anakin_in(port, 0x18) & PARITY); ++ switch (paritycode) { ++ case NONEPARITY: *parity = 'n'; break; ++ case ODDPARITY: *parity = 'o'; break; ++ case EVENPARITY: *parity = 'e'; break; ++ } ++ *bits = 8; ++} ++ ++static int __init ++anakin_console_setup(struct console *co, char *options) ++{ ++ struct uart_port *port; ++ int baud = CONFIG_ANAKIN_DEFAULT_BAUDRATE; ++ int bits = 8; ++ int parity = 'n'; ++ ++ /* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++ port = uart_get_console(anakin_ports, UART_NR, co); ++ ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits); ++ else ++ anakin_console_get_options(port, &baud, &parity, &bits); ++ ++ return uart_set_options(port, co, baud, parity, bits); ++} ++ ++static struct console anakin_console = { ++ name: SERIAL_ANAKIN_NAME, ++ write: anakin_console_write, ++ device: anakin_console_device, ++ setup: anakin_console_setup, ++ flags: CON_PRINTBUFFER, ++ index: -1, ++}; ++ ++void __init ++anakin_console_init(void) ++{ ++ register_console(&anakin_console); ++} ++ ++#define ANAKIN_CONSOLE &anakin_console ++#else ++#define ANAKIN_CONSOLE NULL ++#endif ++ ++static struct uart_register anakin_reg = { ++ normal_major: SERIAL_ANAKIN_MAJOR, ++ normal_name: SERIAL_ANAKIN_NAME, ++ normal_driver: &normal, ++ callout_major: CALLOUT_ANAKIN_MAJOR, ++ callout_name: CALLOUT_ANAKIN_NAME, ++ callout_driver: &callout, ++ table: anakin_table, ++ termios: anakin_termios, ++ termios_locked: anakin_termios_locked, ++ minor: SERIAL_ANAKIN_MINOR, ++ nr: UART_NR, ++ state: anakin_state, ++ port: anakin_ports, ++ cons: ANAKIN_CONSOLE, ++}; ++ ++static int __init ++anakin_init(void) ++{ ++ return uart_register_port(&anakin_reg); ++} ++ ++__initcall(anakin_init); ++ ++MODULE_DESCRIPTION("Anakin serial driver"); ++MODULE_AUTHOR("Tak-Shing Chan "); ++MODULE_SUPPORTED_DEVICE("ttyAN"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/serial/clps711x.c linux-2.4.26-vrs1/drivers/serial/clps711x.c +--- linux-2.4.26/drivers/serial/clps711x.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/clps711x.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,635 @@ ++/* ++ * linux/drivers/char/serial_clps711x.c ++ * ++ * Driver for CLPS711x serial ports ++ * ++ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. ++ * ++ * Copyright 1999 ARM Limited ++ * Copyright (C) 2000 Deep Blue Solutions Ltd. ++ * ++ * 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 ++ * ++ * $Id: clps711x.c,v 1.12.2.2 2002/10/24 09:53:25 rmk Exp $ ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#define UART_NR 2 ++ ++#define SERIAL_CLPS711X_NAME "ttyAM" ++#define SERIAL_CLPS711X_MAJOR 204 ++#define SERIAL_CLPS711X_MINOR 16 ++#define SERIAL_CLPS711X_NR UART_NR ++ ++#define CALLOUT_CLPS711X_NAME "cuaam" ++#define CALLOUT_CLPS711X_MAJOR 205 ++#define CALLOUT_CLPS711X_MINOR 16 ++#define CALLOUT_CLPS711X_NR UART_NR ++ ++static struct tty_driver normal, callout; ++static struct tty_struct *clps711x_table[UART_NR]; ++static struct termios *clps711x_termios[UART_NR], *clps711x_termios_locked[UART_NR]; ++ ++/* ++ * We use the relevant SYSCON register as a base address for these ports. ++ */ ++#define UBRLCR(port) ((port)->iobase + UBRLCR1 - SYSCON1) ++#define UARTDR(port) ((port)->iobase + UARTDR1 - SYSCON1) ++#define SYSFLG(port) ((port)->iobase + SYSFLG1 - SYSCON1) ++#define SYSCON(port) ((port)->iobase + SYSCON1 - SYSCON1) ++ ++#define TX_IRQ(port) ((port)->irq) ++#define RX_IRQ(port) ((port)->irq + 1) ++ ++#define UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR) ++ ++#define tx_enabled(port) ((port)->unused[0]) ++ ++static void ++clps711xuart_stop_tx(struct uart_port *port, unsigned int tty_stop) ++{ ++ if (tx_enabled(port)) { ++ disable_irq(TX_IRQ(port)); ++ tx_enabled(port) = 0; ++ } ++} ++ ++static void ++clps711xuart_start_tx(struct uart_port *port, unsigned int tty_start) ++{ ++ if (!tx_enabled(port)) { ++ enable_irq(TX_IRQ(port)); ++ tx_enabled(port) = 1; ++ } ++} ++ ++static void clps711xuart_stop_rx(struct uart_port *port) ++{ ++ disable_irq(RX_IRQ(port)); ++} ++ ++static void clps711xuart_enable_ms(struct uart_port *port) ++{ ++} ++ ++static void clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct uart_port *port = dev_id; ++ struct tty_struct *tty = port->info->tty; ++ unsigned int status, ch, flg, ignored = 0; ++ ++ status = clps_readl(SYSFLG(port)); ++ while (!(status & SYSFLG_URXFE)) { ++ ch = clps_readl(UARTDR(port)); ++ ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) ++ goto ignore_char; ++ port->icount.rx++; ++ ++ flg = TTY_NORMAL; ++ ++ /* ++ * Note that the error handling code is ++ * out of the main execution path ++ */ ++ if (ch & UART_ANY_ERR) ++ goto handle_error; ++ ++ if (uart_handle_sysrq_char(port, ch, regs)) ++ goto ignore_char; ++ ++ error_return: ++ *tty->flip.flag_buf_ptr++ = flg; ++ *tty->flip.char_buf_ptr++ = ch; ++ tty->flip.count++; ++ ignore_char: ++ status = clps_readl(SYSFLG(port)); ++ } ++ out: ++ tty_flip_buffer_push(tty); ++ return; ++ ++ handle_error: ++ if (ch & UARTDR_PARERR) ++ port->icount.parity++; ++ else if (ch & UARTDR_FRMERR) ++ port->icount.frame++; ++ if (ch & UARTDR_OVERR) ++ port->icount.overrun++; ++ ++ if (ch & port->ignore_status_mask) { ++ if (++ignored > 100) ++ goto out; ++ goto ignore_char; ++ } ++ ch &= port->read_status_mask; ++ ++ if (ch & UARTDR_PARERR) ++ flg = TTY_PARITY; ++ else if (ch & UARTDR_FRMERR) ++ flg = TTY_FRAME; ++ ++ if (ch & UARTDR_OVERR) { ++ /* ++ * CHECK: does overrun affect the current character? ++ * ASSUMPTION: it does not. ++ */ ++ *tty->flip.flag_buf_ptr++ = flg; ++ *tty->flip.char_buf_ptr++ = ch; ++ tty->flip.count++; ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) ++ goto ignore_char; ++ ch = 0; ++ flg = TTY_OVERRUN; ++ } ++#ifdef SUPPORT_SYSRQ ++ port->sysrq = 0; ++#endif ++ goto error_return; ++} ++ ++static void clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct uart_port *port = dev_id; ++ struct circ_buf *xmit = &port->info->xmit; ++ int count; ++ ++ if (port->x_char) { ++ clps_writel(port->x_char, UARTDR(port)); ++ port->icount.tx++; ++ port->x_char = 0; ++ return; ++ } ++ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { ++ clps711xuart_stop_tx(port, 0); ++ return; ++ } ++ ++ count = port->fifosize >> 1; ++ do { ++ clps_writel(xmit->buf[xmit->tail], UARTDR(port)); ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ port->icount.tx++; ++ if (uart_circ_empty(xmit)) ++ break; ++ } while (--count > 0); ++ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(port); ++ ++ if (uart_circ_empty(xmit)) ++ clps711xuart_stop_tx(port, 0); ++} ++ ++static unsigned int clps711xuart_tx_empty(struct uart_port *port) ++{ ++ unsigned int status = clps_readl(SYSFLG(port)); ++ return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT; ++} ++ ++static unsigned int clps711xuart_get_mctrl(struct uart_port *port) ++{ ++ unsigned int port_addr; ++ unsigned int result = 0; ++ unsigned int status; ++ ++ port_addr = SYSFLG(port); ++ if (port_addr == SYSFLG1) { ++ status = clps_readl(SYSFLG1); ++ if (status & SYSFLG1_DCD) ++ result |= TIOCM_CAR; ++ if (status & SYSFLG1_DSR) ++ result |= TIOCM_DSR; ++ if (status & SYSFLG1_CTS) ++ result |= TIOCM_CTS; ++ } ++ ++ return result; ++} ++ ++static void ++clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl) ++{ ++} ++ ++static void clps711xuart_break_ctl(struct uart_port *port, int break_state) ++{ ++ unsigned long flags; ++ unsigned int ubrlcr; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ ubrlcr = clps_readl(UBRLCR(port)); ++ if (break_state == -1) ++ ubrlcr |= UBRLCR_BREAK; ++ else ++ ubrlcr &= ~UBRLCR_BREAK; ++ clps_writel(ubrlcr, UBRLCR(port)); ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static int clps711xuart_startup(struct uart_port *port) ++{ ++ unsigned int syscon; ++ int retval; ++ ++ tx_enabled(port) = 1; ++ ++ /* ++ * Allocate the IRQs ++ */ ++ retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0, ++ "clps711xuart_tx", port); ++ if (retval) ++ return retval; ++ ++ retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0, ++ "clps711xuart_rx", port); ++ if (retval) { ++ free_irq(TX_IRQ(port), port); ++ return retval; ++ } ++ ++ /* ++ * enable the port ++ */ ++ syscon = clps_readl(SYSCON(port)); ++ syscon |= SYSCON_UARTEN; ++ clps_writel(syscon, SYSCON(port)); ++ ++ return 0; ++} ++ ++static void clps711xuart_shutdown(struct uart_port *port) ++{ ++ unsigned int ubrlcr, syscon; ++ ++ /* ++ * Free the interrupt ++ */ ++ free_irq(TX_IRQ(port), port); /* TX interrupt */ ++ free_irq(RX_IRQ(port), port); /* RX interrupt */ ++ ++ /* ++ * disable the port ++ */ ++ syscon = clps_readl(SYSCON(port)); ++ syscon &= ~SYSCON_UARTEN; ++ clps_writel(syscon, SYSCON(port)); ++ ++ /* ++ * disable break condition and fifos ++ */ ++ ubrlcr = clps_readl(UBRLCR(port)); ++ ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK); ++ clps_writel(ubrlcr, UBRLCR(port)); ++} ++ ++static void clps711xuart_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot) ++{ ++ unsigned int ubrlcr; ++ unsigned long flags; ++ ++#if DEBUG ++ printk("clps711xuart_change_speed(cflag=0x%x, iflag=0x%x, quot=%d) called\n", ++ cflag, iflag, quot); ++#endif ++ /* byte size and parity */ ++ switch (cflag & CSIZE) { ++ case CS5: ++ ubrlcr = UBRLCR_WRDLEN5; ++ break; ++ case CS6: ++ ubrlcr = UBRLCR_WRDLEN6; ++ break; ++ case CS7: ++ ubrlcr = UBRLCR_WRDLEN7; ++ break; ++ default: // CS8 ++ ubrlcr = UBRLCR_WRDLEN8; ++ break; ++ } ++ if (cflag & CSTOPB) ++ ubrlcr |= UBRLCR_XSTOP; ++ if (cflag & PARENB) { ++ ubrlcr |= UBRLCR_PRTEN; ++ if (!(cflag & PARODD)) ++ ubrlcr |= UBRLCR_EVENPRT; ++ } ++ if (port->fifosize > 1) ++ ubrlcr |= UBRLCR_FIFOEN; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ ++ port->read_status_mask = UARTDR_OVERR; ++ if (iflag & INPCK) ++ port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR; ++ ++ /* ++ * Characters to ignore ++ */ ++ port->ignore_status_mask = 0; ++ if (iflag & IGNPAR) ++ port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR; ++ if (iflag & IGNBRK) { ++ /* ++ * If we're ignoring parity and break indicators, ++ * ignore overruns to (for real raw support). ++ */ ++ if (iflag & IGNPAR) ++ port->ignore_status_mask |= UARTDR_OVERR; ++ } ++ ++ quot -= 1; ++ ++ clps_writel(ubrlcr | quot, UBRLCR(port)); ++ ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static const char *clps711xuart_type(struct uart_port *port) ++{ ++ return port->type == PORT_CLPS711X ? "CLPS711x" : NULL; ++} ++ ++/* ++ * Configure/autoconfigure the port. ++ */ ++static void clps711xuart_config_port(struct uart_port *port, int flags) ++{ ++ if (flags & UART_CONFIG_TYPE) ++ port->type = PORT_CLPS711X; ++} ++ ++static void clps711xuart_release_port(struct uart_port *port) ++{ ++} ++ ++static int clps711xuart_request_port(struct uart_port *port) ++{ ++ return 0; ++} ++ ++static struct uart_ops clps711x_pops = { ++ .tx_empty = clps711xuart_tx_empty, ++ .set_mctrl = clps711xuart_set_mctrl_null, ++ .get_mctrl = clps711xuart_get_mctrl, ++ .stop_tx = clps711xuart_stop_tx, ++ .start_tx = clps711xuart_start_tx, ++ .stop_rx = clps711xuart_stop_rx, ++ .enable_ms = clps711xuart_enable_ms, ++ .break_ctl = clps711xuart_break_ctl, ++ .startup = clps711xuart_startup, ++ .shutdown = clps711xuart_shutdown, ++ .change_speed = clps711xuart_change_speed, ++ .type = clps711xuart_type, ++ .config_port = clps711xuart_config_port, ++ .release_port = clps711xuart_release_port, ++ .request_port = clps711xuart_request_port, ++}; ++ ++static struct uart_port clps711x_ports[UART_NR] = { ++ { ++ .iobase = SYSCON1, ++ .irq = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */ ++ .uartclk = 3686400, ++ .fifosize = 16, ++ .ops = &clps711x_pops, ++ .line = 0, ++ .flags = ASYNC_BOOT_AUTOCONF, ++ }, ++ { ++ .iobase = SYSCON2, ++ .irq = IRQ_UTXINT2, /* IRQ_URXINT2 */ ++ .uartclk = 3686400, ++ .fifosize = 16, ++ .ops = &clps711x_pops, ++ .line = 1, ++ .flags = ASYNC_BOOT_AUTOCONF, ++ } ++}; ++ ++#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE ++/* ++ * Print a string to the serial port trying not to disturb ++ * any possible real use of the port... ++ * ++ * The console_lock must be held when we get here. ++ * ++ * Note that this is called with interrupts already disabled ++ */ ++static void ++clps711xuart_console_write(struct console *co, const char *s, ++ unsigned int count) ++{ ++ struct uart_port *port = clps711x_ports + co->index; ++ unsigned int status, syscon; ++ int i; ++ ++ /* ++ * Ensure that the port is enabled. ++ */ ++ syscon = clps_readl(SYSCON(port)); ++ clps_writel(syscon | SYSCON_UARTEN, SYSCON(port)); ++ ++ /* ++ * Now, do each character ++ */ ++ for (i = 0; i < count; i++) { ++ do { ++ status = clps_readl(SYSFLG(port)); ++ } while (status & SYSFLG_UTXFF); ++ clps_writel(s[i], UARTDR(port)); ++ if (s[i] == '\n') { ++ do { ++ status = clps_readl(SYSFLG(port)); ++ } while (status & SYSFLG_UTXFF); ++ clps_writel('\r', UARTDR(port)); ++ } ++ } ++ ++ /* ++ * Finally, wait for transmitter to become empty ++ * and restore the uart state. ++ */ ++ do { ++ status = clps_readl(SYSFLG(port)); ++ } while (status & SYSFLG_UBUSY); ++ ++ clps_writel(syscon, SYSCON(port)); ++} ++ ++static kdev_t clps711xuart_console_device(struct console *co) ++{ ++ return MKDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR + co->index); ++} ++ ++static void __init ++clps711xuart_console_get_options(struct uart_port *port, int *baud, ++ int *parity, int *bits) ++{ ++ if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { ++ unsigned int ubrlcr, quot; ++ ++ ubrlcr = clps_readl(UBRLCR(port)); ++ ++ *parity = 'n'; ++ if (ubrlcr & UBRLCR_PRTEN) { ++ if (ubrlcr & UBRLCR_EVENPRT) ++ *parity = 'e'; ++ else ++ *parity = 'o'; ++ } ++ ++ if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7) ++ *bits = 7; ++ else ++ *bits = 8; ++ ++ quot = ubrlcr & UBRLCR_BAUD_MASK; ++ *baud = port->uartclk / (16 * (quot + 1)); ++ } ++} ++ ++static int __init clps711xuart_console_setup(struct console *co, char *options) ++{ ++ struct uart_port *port; ++ int baud = 38400; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++ ++ /* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++ port = uart_get_console(clps711x_ports, UART_NR, co); ++ ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ else ++ clps711xuart_console_get_options(port, &baud, &parity, &bits); ++ ++ return uart_set_options(port, co, baud, parity, bits, flow); ++} ++ ++static struct console clps711x_console = { ++ .name = SERIAL_CLPS711X_NAME, ++ .write = clps711xuart_console_write, ++ .device = clps711xuart_console_device, ++ .setup = clps711xuart_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++}; ++ ++void __init clps711xuart_console_init(void) ++{ ++ register_console(&clps711x_console); ++} ++ ++#define CLPS711X_CONSOLE &clps711x_console ++#else ++#define CLPS711X_CONSOLE NULL ++#endif ++ ++static struct uart_driver clps711x_reg = { ++#ifdef CONFIG_DEVFS_FS ++ .normal_name = SERIAL_CLPS711X_NAME, ++ .callout_name = CALLOUT_CLPS711X_NAME, ++#else ++ .normal_name = SERIAL_CLPS711X_NAME, ++ .callout_name = CALLOUT_CLPS711X_NAME, ++#endif ++ ++ .normal_major = SERIAL_CLPS711X_MAJOR, ++ .normal_driver = &normal, ++ .callout_major = CALLOUT_CLPS711X_MAJOR, ++ .callout_driver = &callout, ++ ++ .table = clps711x_table, ++ .termios = clps711x_termios, ++ .termios_locked = clps711x_termios_locked, ++ ++ .minor = SERIAL_CLPS711X_MINOR, ++ .nr = UART_NR, ++ ++ .cons = CLPS711X_CONSOLE, ++}; ++ ++static int __init clps711xuart_init(void) ++{ ++ int ret, i; ++ ++ printk(KERN_INFO "Serial: CLPS711x driver\n"); ++ ++ ret = uart_register_driver(&clps711x_reg); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < UART_NR; i++) ++ uart_add_one_port(&clps711x_reg, &clps711x_ports[i]); ++ ++ return 0; ++} ++ ++static void __exit clps711xuart_exit(void) ++{ ++ int i; ++ ++ for (i = 0; i < UART_NR; i++) ++ uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]); ++ ++ uart_unregister_driver(&clps711x_reg); ++} ++ ++module_init(clps711xuart_init); ++module_exit(clps711xuart_exit); ++ ++EXPORT_NO_SYMBOLS; ++ ++MODULE_AUTHOR("Deep Blue Solutions Ltd"); ++MODULE_DESCRIPTION("CLPS-711x generic serial driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/serial/core.c linux-2.4.26-vrs1/drivers/serial/core.c +--- linux-2.4.26/drivers/serial/core.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/core.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,2584 @@ ++/* ++ * linux/drivers/serial/core.c ++ * ++ * Driver core for serial ports ++ * ++ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. ++ * ++ * Copyright 1999 ARM Limited ++ * Copyright (C) 2000-2001 Deep Blue Solutions Ltd. ++ * ++ * 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 ++ * ++ * $Id: core.c,v 1.20.2.5 2002/03/13 15:22:26 rmk Exp $ ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#undef DEBUG ++#ifdef DEBUG ++#define DPRINTK(x...) printk(x) ++#else ++#define DPRINTK(x...) do { } while (0) ++#endif ++ ++#ifndef CONFIG_PM ++#define pm_access(pm) do { } while (0) ++#define pm_unregister(pm) do { } while (0) ++#endif ++ ++/* ++ * This is used to lock changes in serial line configuration. ++ */ ++static DECLARE_MUTEX(port_sem); ++ ++#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) ++ ++#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) ++ ++#ifdef CONFIG_SERIAL_CORE_CONSOLE ++#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) ++#else ++#define uart_console(port) (0) ++#endif ++ ++static void uart_change_speed(struct uart_state *state, struct termios *old_termios); ++static void uart_wait_until_sent(struct tty_struct *tty, int timeout); ++static void uart_change_pm(struct uart_state *state, int pm_state); ++ ++/* ++ * This routine is used by the interrupt handler to schedule processing in ++ * the software interrupt portion of the driver. ++ */ ++void uart_write_wakeup(struct uart_port *port) ++{ ++ struct uart_info *info = port->info; ++ tasklet_schedule(&info->tlet); ++} ++ ++static void uart_stop(struct tty_struct *tty) ++{ ++ struct uart_state *state = tty->driver_data; ++ struct uart_port *port = state->port; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ port->ops->stop_tx(port, 1); ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static void __uart_start(struct tty_struct *tty) ++{ ++ struct uart_state *state = tty->driver_data; ++ struct uart_port *port = state->port; ++ ++ if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && ++ !tty->stopped && !tty->hw_stopped) ++ port->ops->start_tx(port, 1); ++} ++ ++static void uart_start(struct tty_struct *tty) ++{ ++ struct uart_state *state = tty->driver_data; ++ struct uart_port *port = state->port; ++ unsigned long flags; ++ ++ pm_access(state->pm); ++ ++ spin_lock_irqsave(&port->lock, flags); ++ __uart_start(tty); ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static void uart_tasklet_action(unsigned long data) ++{ ++ struct uart_state *state = (struct uart_state *)data; ++ struct tty_struct *tty; ++ ++ tty = state->info->tty; ++ if (tty) { ++ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && ++ tty->ldisc.write_wakeup) ++ tty->ldisc.write_wakeup(tty); ++ wake_up_interruptible(&tty->write_wait); ++ } ++} ++ ++static inline void ++uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) ++{ ++ unsigned long flags; ++ unsigned int old; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ old = port->mctrl; ++ port->mctrl = (old & ~clear) | set; ++ if (old != port->mctrl) ++ port->ops->set_mctrl(port, port->mctrl); ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++#define uart_set_mctrl(port,set) uart_update_mctrl(port,set,0) ++#define uart_clear_mctrl(port,clear) uart_update_mctrl(port,0,clear) ++ ++static inline unsigned int uart_get_altspeed(struct uart_port *port) ++{ ++ unsigned int flags = port->flags & UPF_SPD_MASK; ++ unsigned int altbaud = 0; ++ ++ if (flags == ASYNC_SPD_HI) ++ altbaud = 57600; ++ if (flags == ASYNC_SPD_VHI) ++ altbaud = 115200; ++ if (flags == ASYNC_SPD_SHI) ++ altbaud = 230400; ++ if (flags == ASYNC_SPD_WARP) ++ altbaud = 460800; ++ ++ return altbaud; ++} ++ ++/* ++ * Startup the port. This will be called once per open. All calls ++ * will be serialised by the per-port semaphore. ++ */ ++static int uart_startup(struct uart_state *state, int init_hw) ++{ ++ struct uart_info *info = state->info; ++ struct uart_port *port = state->port; ++ unsigned long page; ++ int retval = 0; ++ ++ if (info->flags & UIF_INITIALIZED) ++ return 0; ++ ++ /* ++ * Set the TTY IO error marker - we will only clear this ++ * once we have successfully opened the port. Also set ++ * up the tty->alt_speed kludge ++ */ ++ if (info->tty) ++ set_bit(TTY_IO_ERROR, &info->tty->flags); ++ ++ if (port->type == PORT_UNKNOWN) ++ return 0; ++ ++ /* ++ * Initialise and allocate the transmit and temporary ++ * buffer. ++ */ ++ if (!info->xmit.buf) { ++ page = get_zeroed_page(GFP_KERNEL); ++ if (!page) ++ return -ENOMEM; ++ ++ info->xmit.buf = (unsigned char *) page; ++ info->tmpbuf = info->xmit.buf + UART_XMIT_SIZE; ++ init_MUTEX(&info->tmpbuf_sem); ++ uart_circ_clear(&info->xmit); ++ } ++ ++ port->mctrl = 0; ++ ++ retval = port->ops->startup(port); ++ if (retval == 0) { ++ if (init_hw) { ++ /* ++ * Initialise the hardware port settings. ++ */ ++ uart_change_speed(state, NULL); ++ ++ /* ++ * Setup the RTS and DTR signals once the ++ * port is open and ready to respond. ++ */ ++ if (info->tty->termios->c_cflag & CBAUD) ++ uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); ++ } ++ ++ info->flags |= UIF_INITIALIZED; ++ ++ clear_bit(TTY_IO_ERROR, &info->tty->flags); ++ } ++ ++ if (retval && capable(CAP_SYS_ADMIN)) ++ retval = 0; ++ ++ return retval; ++} ++ ++/* ++ * This routine will shutdown a serial port; interrupts are disabled, and ++ * DTR is dropped if the hangup on close termio flag is on. Calls to ++ * uart_shutdown are serialised by the per-port semaphore. ++ */ ++static void uart_shutdown(struct uart_state *state) ++{ ++ struct uart_info *info = state->info; ++ struct uart_port *port = state->port; ++ ++ if (!(info->flags & UIF_INITIALIZED)) ++ return; ++ ++ /* ++ * Turn off DTR and RTS early. ++ */ ++ if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) ++ uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); ++ ++ /* ++ * clear delta_msr_wait queue to avoid mem leaks: we may free ++ * the irq here so the queue might never be woken up. Note ++ * that we won't end up waiting on delta_msr_wait again since ++ * any outstanding file descriptors should be pointing at ++ * hung_up_tty_fops now. ++ */ ++ wake_up_interruptible(&info->delta_msr_wait); ++ ++ /* ++ * Free the IRQ and disable the port. ++ */ ++ port->ops->shutdown(port); ++ ++ /* ++ * Free the transmit buffer page. ++ */ ++ if (info->xmit.buf) { ++ free_page((unsigned long)info->xmit.buf); ++ info->xmit.buf = NULL; ++ info->tmpbuf = NULL; ++ } ++ ++ /* ++ * kill off our tasklet ++ */ ++ tasklet_kill(&info->tlet); ++ if (info->tty) ++ set_bit(TTY_IO_ERROR, &info->tty->flags); ++ ++ info->flags &= ~UIF_INITIALIZED; ++} ++ ++/** ++ * uart_update_timeout - update per-port FIFO timeout. ++ * @port: uart_port structure describing the port. ++ * @cflag: termios cflag value ++ * @quot: uart clock divisor quotient ++ * ++ * Set the port FIFO timeout value. The @cflag value should ++ * reflect the actual hardware settings. ++ */ ++void ++uart_update_timeout(struct uart_port *port, unsigned int cflag, ++ unsigned int baud) ++{ ++ unsigned int bits; ++ ++ /* byte size and parity */ ++ switch (cflag & CSIZE) { ++ case CS5: ++ bits = 7; ++ break; ++ case CS6: ++ bits = 8; ++ break; ++ case CS7: ++ bits = 9; ++ break; ++ default: ++ bits = 10; ++ break; // CS8 ++ } ++ ++ if (cflag & CSTOPB) ++ bits++; ++ if (cflag & PARENB) ++ bits++; ++ ++ /* ++ * The total number of bits to be transmitted in the fifo. ++ */ ++ bits = bits * port->fifosize; ++ ++ /* ++ * Figure the timeout to send the above number of bits. ++ * Add .02 seconds of slop ++ */ ++ port->timeout = (HZ * bits) / baud + HZ/50; ++} ++ ++EXPORT_SYMBOL(uart_update_timeout); ++ ++static inline u_int uart_calculate_quot(struct uart_port *port, u_int baud) ++{ ++ u_int quot; ++ ++ /* Special case: B0 rate */ ++ if (!baud) ++ baud = 9600; ++ ++ /* Old HI/VHI/custom speed handling */ ++ if (baud == 38400 && ++ ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) ++ quot = port->custom_divisor; ++ else ++ quot = port->uartclk / (16 * baud); ++ ++ return quot; ++} ++ ++static void ++uart_change_speed(struct uart_state *state, struct termios *old_termios) ++{ ++ struct tty_struct *tty = state->info->tty; ++ struct uart_port *port = state->port; ++ struct termios *termios; ++ unsigned int quot, baud, cflag, try; ++ ++ /* ++ * If we have no tty, termios, or the port does not exist, ++ * then we can't set the parameters for this port. ++ */ ++ if (!tty || !tty->termios || port->type == PORT_UNKNOWN) ++ return; ++ ++ termios = tty->termios; ++ ++ cflag = termios->c_cflag; ++ ++ for (try = 0; try < 2; try ++) { ++ /* Determine divisor based on baud rate */ ++ baud = tty_get_baud_rate(tty); ++ quot = uart_calculate_quot(port, baud); ++ if (quot) ++ break; ++ ++ /* ++ * Oops, the quotient was zero. Try again with ++ * the old baud rate if possible. ++ */ ++ termios->c_cflag &= ~CBAUD; ++ if (old_termios) { ++ termios->c_cflag |= ++ (old_termios->c_cflag & CBAUD); ++ old_termios = NULL; ++ continue; ++ } ++ ++ /* ++ * As a last resort, if the quotient is zero, ++ * default to 9600 bps ++ */ ++ termios->c_cflag |= B9600; ++ } ++ ++ uart_update_timeout(port, cflag, port->uartclk / (16 * quot)); ++ ++ if (termios->c_cflag & CRTSCTS) ++ state->info->flags |= UIF_CTS_FLOW; ++ else ++ state->info->flags &= ~UIF_CTS_FLOW; ++ if (termios->c_cflag & CLOCAL) ++ state->info->flags &= ~UIF_CHECK_CD; ++ else ++ state->info->flags |= UIF_CHECK_CD; ++ ++ /* ++ * Set up parity check flag ++ */ ++ pm_access(state->pm); ++ ++ port->ops->change_speed(port, cflag, termios->c_iflag, quot); ++} ++ ++static inline void ++__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c) ++{ ++ unsigned long flags; ++ ++ if (!circ->buf) ++ return; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ if (uart_circ_chars_free(circ) != 0) { ++ circ->buf[circ->head] = c; ++ circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); ++ } ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ ++static inline int ++__uart_user_write(struct uart_port *port, struct circ_buf *circ, ++ const unsigned char *buf, int count) ++{ ++ unsigned long flags; ++ int c, ret = 0; ++ ++ if (down_interruptible(&port->info->tmpbuf_sem)) ++ return -EINTR; ++ ++ while (1) { ++ int c1; ++ c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); ++ if (count < c) ++ c = count; ++ if (c <= 0) ++ break; ++ ++ c -= copy_from_user(port->info->tmpbuf, buf, c); ++ if (!c) { ++ if (!ret) ++ ret = -EFAULT; ++ break; ++ } ++ spin_lock_irqsave(&port->lock, flags); ++ c1 = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); ++ if (c1 < c) ++ c = c1; ++ memcpy(circ->buf + circ->head, port->info->tmpbuf, c); ++ circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); ++ spin_unlock_irqrestore(&port->lock, flags); ++ buf += c; ++ count -= c; ++ ret += c; ++ } ++ up(&port->info->tmpbuf_sem); ++ ++ return ret; ++} ++ ++static inline int ++__uart_kern_write(struct uart_port *port, struct circ_buf *circ, ++ const unsigned char *buf, int count) ++{ ++ unsigned long flags; ++ int c, ret = 0; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ while (1) { ++ c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); ++ if (count < c) ++ c = count; ++ if (c <= 0) ++ break; ++ memcpy(circ->buf + circ->head, buf, c); ++ circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); ++ buf += c; ++ count -= c; ++ ret += c; ++ } ++ spin_unlock_irqrestore(&port->lock, flags); ++ ++ return ret; ++} ++ ++static void uart_put_char(struct tty_struct *tty, unsigned char ch) ++{ ++ struct uart_state *state = tty->driver_data; ++ ++ __uart_put_char(state->port, &state->info->xmit, ch); ++} ++ ++static void uart_flush_chars(struct tty_struct *tty) ++{ ++ uart_start(tty); ++} ++ ++static int ++uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, ++ int count) ++{ ++ struct uart_state *state = tty->driver_data; ++ int ret; ++ ++ if (!state->info->xmit.buf) ++ return 0; ++ ++ if (from_user) ++ ret = __uart_user_write(state->port, &state->info->xmit, buf, count); ++ else ++ ret = __uart_kern_write(state->port, &state->info->xmit, buf, count); ++ ++ uart_start(tty); ++ return ret; ++} ++ ++static int uart_write_room(struct tty_struct *tty) ++{ ++ struct uart_state *state = tty->driver_data; ++ ++ return uart_circ_chars_free(&state->info->xmit); ++} ++ ++static int uart_chars_in_buffer(struct tty_struct *tty) ++{ ++ struct uart_state *state = tty->driver_data; ++ ++ return uart_circ_chars_pending(&state->info->xmit); ++} ++ ++static void uart_flush_buffer(struct tty_struct *tty) ++{ ++ struct uart_state *state = tty->driver_data; ++ struct uart_port *port = state->port; ++ unsigned long flags; ++ ++ DPRINTK("uart_flush_buffer(%d) called\n", ++ MINOR(tty->device) - tty->driver.minor_start); ++ ++ spin_lock_irqsave(&port->lock, flags); ++ uart_circ_clear(&state->info->xmit); ++ spin_unlock_irqrestore(&port->lock, flags); ++ wake_up_interruptible(&tty->write_wait); ++ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && ++ tty->ldisc.write_wakeup) ++ (tty->ldisc.write_wakeup)(tty); ++} ++ ++/* ++ * This function is used to send a high-priority XON/XOFF character to ++ * the device ++ */ ++static void uart_send_xchar(struct tty_struct *tty, char ch) ++{ ++ struct uart_state *state = tty->driver_data; ++ struct uart_port *port = state->port; ++ unsigned long flags; ++ ++ if (port->ops->send_xchar) ++ port->ops->send_xchar(port, ch); ++ else { ++ port->x_char = ch; ++ if (ch) { ++ spin_lock_irqsave(&port->lock, flags); ++ port->ops->start_tx(port, 0); ++ spin_unlock_irqrestore(&port->lock, flags); ++ } ++ } ++} ++ ++static void uart_throttle(struct tty_struct *tty) ++{ ++ struct uart_state *state = tty->driver_data; ++ ++ if (I_IXOFF(tty)) ++ uart_send_xchar(tty, STOP_CHAR(tty)); ++ ++ if (tty->termios->c_cflag & CRTSCTS) ++ uart_clear_mctrl(state->port, TIOCM_RTS); ++} ++ ++static void uart_unthrottle(struct tty_struct *tty) ++{ ++ struct uart_state *state = tty->driver_data; ++ struct uart_port *port = state->port; ++ ++ if (I_IXOFF(tty)) { ++ if (port->x_char) ++ port->x_char = 0; ++ else ++ uart_send_xchar(tty, START_CHAR(tty)); ++ } ++ ++ if (tty->termios->c_cflag & CRTSCTS) ++ uart_set_mctrl(port, TIOCM_RTS); ++} ++ ++static int uart_get_info(struct uart_state *state, struct serial_struct *retinfo) ++{ ++ struct uart_port *port = state->port; ++ struct serial_struct tmp; ++ ++ memset(&tmp, 0, sizeof(tmp)); ++ tmp.type = port->type; ++ tmp.line = port->line; ++ tmp.port = port->iobase; ++ if (HIGH_BITS_OFFSET) ++ tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; ++ tmp.irq = port->irq; ++ tmp.flags = port->flags; ++ tmp.xmit_fifo_size = port->fifosize; ++ tmp.baud_base = port->uartclk / 16; ++ tmp.close_delay = state->close_delay; ++ tmp.closing_wait = state->closing_wait; ++ tmp.custom_divisor = port->custom_divisor; ++ tmp.hub6 = port->hub6; ++ tmp.io_type = port->iotype; ++ tmp.iomem_reg_shift = port->regshift; ++ tmp.iomem_base = (void *)port->mapbase; ++ ++ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) ++ return -EFAULT; ++ return 0; ++} ++ ++static int ++uart_set_info(struct uart_state *state, struct serial_struct *newinfo) ++{ ++ struct serial_struct new_serial; ++ struct uart_port *port = state->port; ++ unsigned long new_port; ++ unsigned int change_irq, change_port, old_flags; ++ unsigned int old_custom_divisor; ++ int retval = 0; ++ ++ if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) ++ return -EFAULT; ++ ++ new_port = new_serial.port; ++ if (HIGH_BITS_OFFSET) ++ new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; ++ ++ new_serial.irq = irq_cannonicalize(new_serial.irq); ++ ++ /* ++ * This semaphore protects state->count. It is also ++ * very useful to prevent opens. Also, take the ++ * port configuration semaphore to make sure that a ++ * module insertion/removal doesn't change anything ++ * under us. ++ */ ++ down(&state->sem); ++ ++ change_irq = new_serial.irq != port->irq; ++ ++ /* ++ * Since changing the 'type' of the port changes its resource ++ * allocations, we should treat type changes the same as ++ * IO port changes. ++ */ ++ change_port = new_port != port->iobase || ++ (unsigned long)new_serial.iomem_base != port->mapbase || ++ new_serial.hub6 != port->hub6 || ++ new_serial.io_type != port->iotype || ++ new_serial.iomem_reg_shift != port->regshift || ++ new_serial.type != port->type; ++ ++ old_flags = port->flags; ++ old_custom_divisor = port->custom_divisor; ++ ++ if (!capable(CAP_SYS_ADMIN)) { ++ retval = -EPERM; ++ if (change_irq || change_port || ++ (new_serial.baud_base != port->uartclk / 16) || ++ (new_serial.close_delay != state->close_delay) || ++ (new_serial.closing_wait != state->closing_wait) || ++ (new_serial.xmit_fifo_size != port->fifosize) || ++ (((new_serial.flags ^ old_flags) & ~UPF_USR_MASK) != 0)) ++ goto exit; ++ port->flags = ((port->flags & ~UPF_USR_MASK) | ++ (new_serial.flags & UPF_USR_MASK)); ++ port->custom_divisor = new_serial.custom_divisor; ++ goto check_and_exit; ++ } ++ ++ /* ++ * Ask the low level driver to verify the settings. ++ */ ++ if (port->ops->verify_port) ++ retval = port->ops->verify_port(port, &new_serial); ++ ++ if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || ++ (new_serial.baud_base < 9600)) ++ retval = -EINVAL; ++ ++ if (retval) ++ goto exit; ++ ++ if (change_port || change_irq) { ++ retval = -EBUSY; ++ ++ /* ++ * Make sure that we are the sole user of this port. ++ */ ++ if (uart_users(state) > 1) ++ goto exit; ++ ++ /* ++ * We need to shutdown the serial port at the old ++ * port/type/irq combination. ++ */ ++ uart_shutdown(state); ++ } ++ ++ if (change_port) { ++ unsigned long old_iobase, old_mapbase; ++ unsigned int old_type, old_iotype, old_hub6, old_shift; ++ ++ old_iobase = port->iobase; ++ old_mapbase = port->mapbase; ++ old_type = port->type; ++ old_hub6 = port->hub6; ++ old_iotype = port->iotype; ++ old_shift = port->regshift; ++ ++ /* ++ * Free and release old regions ++ */ ++ if (old_type != PORT_UNKNOWN) ++ port->ops->release_port(port); ++ ++ port->iobase = new_port; ++ port->type = new_serial.type; ++ port->hub6 = new_serial.hub6; ++ port->iotype = new_serial.io_type; ++ port->regshift = new_serial.iomem_reg_shift; ++ port->mapbase = (unsigned long)new_serial.iomem_base; ++ ++ /* ++ * Claim and map the new regions ++ */ ++ if (port->type != PORT_UNKNOWN) { ++ retval = port->ops->request_port(port); ++ } else { ++ /* Always success - Jean II */ ++ retval = 0; ++ } ++ ++ /* ++ * If we fail to request resources for the ++ * new port, try to restore the old settings. ++ */ ++ if (retval && old_type != PORT_UNKNOWN) { ++ port->iobase = old_iobase; ++ port->type = old_type; ++ port->hub6 = old_hub6; ++ port->iotype = old_iotype; ++ port->regshift = old_shift; ++ port->mapbase = old_mapbase; ++ retval = port->ops->request_port(port); ++ /* ++ * If we failed to restore the old settings, ++ * we fail like this. ++ */ ++ if (retval) ++ port->type = PORT_UNKNOWN; ++ ++ /* ++ * We failed anyway. ++ */ ++ retval = -EBUSY; ++ } ++ } ++ ++ port->irq = new_serial.irq; ++ port->uartclk = new_serial.baud_base * 16; ++ port->flags = (port->flags & ~UPF_CHANGE_MASK) | ++ (new_serial.flags & UPF_CHANGE_MASK); ++ port->custom_divisor = new_serial.custom_divisor; ++ state->close_delay = new_serial.close_delay * HZ / 100; ++ state->closing_wait = new_serial.closing_wait * HZ / 100; ++ port->fifosize = new_serial.xmit_fifo_size; ++ if (state->info->tty) ++ state->info->tty->low_latency = ++ (port->flags & UPF_LOW_LATENCY) ? 1 : 0; ++ ++ check_and_exit: ++ retval = 0; ++ if (port->type == PORT_UNKNOWN) ++ goto exit; ++ if (state->info->flags & UIF_INITIALIZED) { ++ if (((old_flags ^ port->flags) & UPF_SPD_MASK) || ++ old_custom_divisor != port->custom_divisor) { ++ state->info->tty->alt_speed = uart_get_altspeed(port); ++ uart_change_speed(state, NULL); ++ } ++ } else ++ retval = uart_startup(state, 1); ++ exit: ++ up(&state->sem); ++ return retval; ++} ++ ++ ++/* ++ * uart_get_lsr_info - get line status register info. ++ * Note: uart_ioctl protects us against hangups. ++ */ ++static int uart_get_lsr_info(struct uart_state *state, unsigned int *value) ++{ ++ struct uart_port *port = state->port; ++ unsigned int result; ++ ++ result = port->ops->tx_empty(port); ++ ++ /* ++ * If we're about to load something into the transmit ++ * register, we'll pretend the transmitter isn't empty to ++ * avoid a race condition (depending on when the transmit ++ * interrupt happens). ++ */ ++ if (port->x_char || ++ ((uart_circ_chars_pending(&state->info->xmit) > 0) && ++ !state->info->tty->stopped && !state->info->tty->hw_stopped)) ++ result &= ~TIOCSER_TEMT; ++ ++ return put_user(result, value); ++} ++ ++static int uart_tiocmget(struct tty_struct *tty, struct file *file) ++{ ++ struct uart_state *state = tty->driver_data; ++ struct uart_port *port = state->port; ++ int result = -EIO; ++ ++ down(&state->sem); ++ if ((!file || !tty_hung_up_p(file)) && ++ !(tty->flags & (1 << TTY_IO_ERROR))) { ++ result = port->mctrl; ++ result |= port->ops->get_mctrl(port); ++ } ++ up(&state->sem); ++ ++ return result; ++} ++ ++static int ++uart_tiocmset(struct tty_struct *tty, struct file *file, ++ unsigned int set, unsigned int clear) ++{ ++ struct uart_state *state = tty->driver_data; ++ struct uart_port *port = state->port; ++ int ret = -EIO; ++ ++ down(&state->sem); ++ if ((!file || !tty_hung_up_p(file)) && ++ !(tty->flags & (1 << TTY_IO_ERROR))) { ++ uart_update_mctrl(port, set, clear); ++ ret = 0; ++ } ++ up(&state->sem); ++ return ret; ++} ++ ++static void uart_break_ctl(struct tty_struct *tty, int break_state) ++{ ++ struct uart_state *state = tty->driver_data; ++ struct uart_port *port = state->port; ++ ++ BUG_ON(!kernel_locked()); ++ ++ down(&state->sem); ++ ++ if (port->type != PORT_UNKNOWN) ++ port->ops->break_ctl(port, break_state); ++ ++ up(&state->sem); ++} ++ ++static int uart_do_autoconfig(struct uart_state *state) ++{ ++ struct uart_port *port = state->port; ++ int flags, ret; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ /* ++ * Take the per-port semaphore. This prevents count from ++ * changing, and hence any extra opens of the port while ++ * we're auto-configuring. ++ */ ++ if (down_interruptible(&state->sem)) ++ return -ERESTARTSYS; ++ ++ ret = -EBUSY; ++ if (uart_users(state) == 1) { ++ uart_shutdown(state); ++ ++ /* ++ * If we already have a port type configured, ++ * we must release its resources. ++ */ ++ if (port->type != PORT_UNKNOWN) ++ port->ops->release_port(port); ++ ++ flags = UART_CONFIG_TYPE; ++ if (port->flags & UPF_AUTO_IRQ) ++ flags |= UART_CONFIG_IRQ; ++ ++ /* ++ * This will claim the ports resources if ++ * a port is found. ++ */ ++ port->ops->config_port(port, flags); ++ ++ ret = uart_startup(state, 1); ++ } ++ up(&state->sem); ++ return ret; ++} ++ ++/* ++ * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change ++ * - mask passed in arg for lines of interest ++ * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) ++ * Caller should use TIOCGICOUNT to see which one it was ++ */ ++static int ++uart_wait_modem_status(struct uart_state *state, unsigned long arg) ++{ ++ struct uart_port *port = state->port; ++ DECLARE_WAITQUEUE(wait, current); ++ struct uart_icount cprev, cnow; ++ int ret; ++ ++ /* ++ * note the counters on entry ++ */ ++ spin_lock_irq(&port->lock); ++ memcpy(&cprev, &port->icount, sizeof(struct uart_icount)); ++ ++ /* ++ * Force modem status interrupts on ++ */ ++ port->ops->enable_ms(port); ++ spin_unlock_irq(&port->lock); ++ ++ add_wait_queue(&state->info->delta_msr_wait, &wait); ++ for (;;) { ++ spin_lock_irq(&port->lock); ++ memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); ++ spin_unlock_irq(&port->lock); ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ++ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ++ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ++ ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { ++ ret = 0; ++ break; ++ } ++ ++ schedule(); ++ ++ /* see if a signal did it */ ++ if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ break; ++ } ++ ++ cprev = cnow; ++ } ++ ++ current->state = TASK_RUNNING; ++ remove_wait_queue(&state->info->delta_msr_wait, &wait); ++ ++ return ret; ++} ++ ++/* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for ++ * RI where only 0->1 is counted. ++ */ ++static int ++uart_get_count(struct uart_state *state, struct serial_icounter_struct *icnt) ++{ ++ struct serial_icounter_struct icount; ++ struct uart_icount cnow; ++ struct uart_port *port = state->port; ++ ++ spin_lock_irq(&port->lock); ++ memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); ++ spin_unlock_irq(&port->lock); ++ ++ icount.cts = cnow.cts; ++ icount.dsr = cnow.dsr; ++ icount.rng = cnow.rng; ++ icount.dcd = cnow.dcd; ++ icount.rx = cnow.rx; ++ icount.tx = cnow.tx; ++ icount.frame = cnow.frame; ++ icount.overrun = cnow.overrun; ++ icount.parity = cnow.parity; ++ icount.brk = cnow.brk; ++ icount.buf_overrun = cnow.buf_overrun; ++ ++ return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; ++} ++ ++/* ++ * Called via sys_ioctl under the BKL. We can use spin_lock_irq() here. ++ */ ++static int ++uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct uart_state *state = tty->driver_data; ++ int ret = -ENOIOCTLCMD; ++ ++ BUG_ON(!kernel_locked()); ++ ++ /* ++ * These ioctls don't rely on the hardware to be present. ++ */ ++ switch (cmd) { ++ case TIOCGSERIAL: ++ ret = uart_get_info(state, (struct serial_struct *)arg); ++ break; ++ ++ case TIOCSSERIAL: ++ ret = uart_set_info(state, (struct serial_struct *)arg); ++ break; ++ ++ case TIOCSERCONFIG: ++ ret = uart_do_autoconfig(state); ++ break; ++ ++ case TIOCSERGWILD: /* obsolete */ ++ case TIOCSERSWILD: /* obsolete */ ++ ret = 0; ++ break; ++ } ++ ++ if (ret != -ENOIOCTLCMD) ++ goto out; ++ ++ if (tty->flags & (1 << TTY_IO_ERROR)) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ /* ++ * The following should only be used when hardware is present. ++ */ ++ switch (cmd) { ++ case TIOCMIWAIT: ++ ret = uart_wait_modem_status(state, arg); ++ break; ++ ++ case TIOCGICOUNT: ++ ret = uart_get_count(state, (struct serial_icounter_struct *)arg); ++ break; ++ ++ case TIOCMGET: ++ { ++ int val; ++ val = uart_tiocmget(tty, filp); ++ if (val >= 0) { ++ ret = put_user(val, (int *)arg); ++ } else { ++ ret = val; ++ } ++ } ++ break; ++ ++ case TIOCMBIS: ++ case TIOCMBIC: ++ case TIOCMSET: ++ { ++ int val, set = 0, clear = 0; ++ ret = get_user(val, (int *)arg); ++ if (ret) ++ break; ++ ++ switch (cmd) { ++ case TIOCMBIS: ++ set = val; ++ break; ++ case TIOCMBIC: ++ clear = val; ++ break; ++ case TIOCMSET: ++ set = val; ++ clear = ~val; ++ break; ++ } ++ ++ set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; ++ clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; ++ ++ ret = uart_tiocmset(tty, filp, set, clear); ++ } ++ break; ++ } ++ ++ if (ret != -ENOIOCTLCMD) ++ goto out; ++ ++ down(&state->sem); ++ ++ if (tty_hung_up_p(filp)) { ++ ret = -EIO; ++ goto out_up; ++ } ++ ++ /* ++ * All these rely on hardware being present and need to be ++ * protected against the tty being hung up. ++ */ ++ switch (cmd) { ++ case TIOCSERGETLSR: /* Get line status register */ ++ ret = uart_get_lsr_info(state, (unsigned int *)arg); ++ break; ++ ++ default: { ++ struct uart_port *port = state->port; ++ if (port->ops->ioctl) ++ ret = port->ops->ioctl(port, cmd, arg); ++ break; ++ } ++ } ++ out_up: ++ up(&state->sem); ++ out: ++ return ret; ++} ++ ++static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios) ++{ ++ struct uart_state *state = tty->driver_data; ++ unsigned long flags; ++ unsigned int cflag = tty->termios->c_cflag; ++ ++ BUG_ON(!kernel_locked()); ++ ++ /* ++ * These are the bits that are used to setup various ++ * flags in the low level driver. ++ */ ++#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) ++ ++ if ((cflag ^ old_termios->c_cflag) == 0 && ++ RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) ++ return; ++ ++ uart_change_speed(state, old_termios); ++ ++ /* Handle transition to B0 status */ ++ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) ++ uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); ++ ++ /* Handle transition away from B0 status */ ++ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { ++ unsigned int mask = TIOCM_DTR; ++ if (!(cflag & CRTSCTS) || ++ !test_bit(TTY_THROTTLED, &tty->flags)) ++ mask |= TIOCM_RTS; ++ uart_set_mctrl(state->port, mask); ++ } ++ ++ /* Handle turning off CRTSCTS */ ++ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { ++ spin_lock_irqsave(&state->port->lock, flags); ++ tty->hw_stopped = 0; ++ __uart_start(tty); ++ spin_unlock_irqrestore(&state->port->lock, flags); ++ } ++ ++#if 0 ++ /* ++ * No need to wake up processes in open wait, since they ++ * sample the CLOCAL flag once, and don't recheck it. ++ * XXX It's not clear whether the current behavior is correct ++ * or not. Hence, this may change..... ++ */ ++ if (!(old_termios->c_cflag & CLOCAL) && ++ (tty->termios->c_cflag & CLOCAL)) ++ wake_up_interruptible(&state->info->open_wait); ++#endif ++} ++ ++/* ++ * In 2.4.5, calls to this will be serialized via the BKL in ++ * linux/drivers/char/tty_io.c:tty_release() ++ * linux/drivers/char/tty_io.c:do_tty_handup() ++ */ ++static void uart_close(struct tty_struct *tty, struct file *filp) ++{ ++ struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; ++ struct uart_state *state = tty->driver_data; ++ struct uart_port *port; ++ ++ BUG_ON(!kernel_locked()); ++ ++ if (!state || !state->port) ++ return; ++ ++ port = state->port; ++ ++ DPRINTK("uart_close(%d) called\n", port->line); ++ ++ down(&state->sem); ++ ++ if (tty_hung_up_p(filp)) ++ goto done; ++ ++ if ((tty->count == 1) && (state->count != 1)) { ++ /* ++ * Uh, oh. tty->count is 1, which means that the tty ++ * structure will be freed. state->count should always ++ * be one in these conditions. If it's greater than ++ * one, we've got real problems, since it means the ++ * serial port won't be shutdown. ++ */ ++ printk("uart_close: bad serial port count; tty->count is 1, " ++ "state->count is %d\n", state->count); ++ state->count = 1; ++ } ++ if (--state->count < 0) { ++ printk("rs_close: bad serial port count for %s%d: %d\n", ++ tty->driver.name, port->line, state->count); ++ state->count = 0; ++ } ++ if (state->count) ++ goto done; ++ ++ /* ++ * Save the termios structure, since this port may have ++ * separate termios for callout and dialin. ++ */ ++ if (state->info->flags & UIF_NORMAL_ACTIVE) ++ state->normal_termios = *tty->termios; ++ if (state->info->flags & UIF_CALLOUT_ACTIVE) ++ state->callout_termios = *tty->termios; ++ ++ /* ++ * Now we wait for the transmit buffer to clear; and we notify ++ * the line discipline to only process XON/XOFF characters by ++ * setting tty->closing. ++ */ ++ tty->closing = 1; ++ ++ if (state->closing_wait != USF_CLOSING_WAIT_NONE) ++ tty_wait_until_sent(tty, state->closing_wait); ++ ++ /* ++ * At this point, we stop accepting input. To do this, we ++ * disable the receive line status interrupts. ++ */ ++ if (state->info->flags & UIF_INITIALIZED) { ++ unsigned long flags; ++ spin_lock_irqsave(&port->lock, flags); ++ port->ops->stop_rx(port); ++ spin_unlock_irqrestore(&port->lock, flags); ++ /* ++ * Before we drop DTR, make sure the UART transmitter ++ * has completely drained; this is especially ++ * important if there is a transmit FIFO! ++ */ ++ uart_wait_until_sent(tty, port->timeout); ++ } ++ ++ uart_shutdown(state); ++ uart_flush_buffer(tty); ++ if (tty->ldisc.flush_buffer) ++ tty->ldisc.flush_buffer(tty); ++ tty->closing = 0; ++ state->info->tty = NULL; ++ ++ if (state->info->blocked_open) { ++ if (state->close_delay) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(state->close_delay); ++ set_current_state(TASK_RUNNING); ++ } ++ } else if (!uart_console(port)) { ++ uart_change_pm(state, 3); ++ } ++ ++ /* ++ * Wake up anyone trying to open this port. ++ */ ++ state->info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CALLOUT_ACTIVE); ++ wake_up_interruptible(&state->info->open_wait); ++ ++ done: ++ up(&state->sem); ++ if (drv->owner) ++ __MOD_DEC_USE_COUNT(drv->owner); ++} ++ ++static void uart_wait_until_sent(struct tty_struct *tty, int timeout) ++{ ++ struct uart_state *state = tty->driver_data; ++ struct uart_port *port = state->port; ++ unsigned long char_time, expire; ++ ++ BUG_ON(!kernel_locked()); ++ ++ if (port->type == PORT_UNKNOWN || port->fifosize == 0) ++ return; ++ ++ /* ++ * Set the check interval to be 1/5 of the estimated time to ++ * send a single character, and make it at least 1. The check ++ * interval should also be less than the timeout. ++ * ++ * Note: we have to use pretty tight timings here to satisfy ++ * the NIST-PCTS. ++ */ ++ char_time = (port->timeout - HZ/50) / port->fifosize; ++ char_time = char_time / 5; ++ if (char_time == 0) ++ char_time = 1; ++ if (timeout && timeout < char_time) ++ char_time = timeout; ++ ++ /* ++ * If the transmitter hasn't cleared in twice the approximate ++ * amount of time to send the entire FIFO, it probably won't ++ * ever clear. This assumes the UART isn't doing flow ++ * control, which is currently the case. Hence, if it ever ++ * takes longer than port->timeout, this is probably due to a ++ * UART bug of some kind. So, we clamp the timeout parameter at ++ * 2*port->timeout. ++ */ ++ if (timeout == 0 || timeout > 2 * port->timeout) ++ timeout = 2 * port->timeout; ++ ++ expire = jiffies + timeout; ++ ++ DPRINTK("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n", ++ port->line, jiffies, expire); ++ ++ /* ++ * Check whether the transmitter is empty every 'char_time'. ++ * 'timeout' / 'expire' give us the maximum amount of time ++ * we wait. ++ */ ++ while (!port->ops->tx_empty(port)) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(char_time); ++ if (signal_pending(current)) ++ break; ++ if (time_after(jiffies, expire)) ++ break; ++ } ++ set_current_state(TASK_RUNNING); /* might not be needed */ ++} ++ ++/* ++ * This is called with the BKL held in ++ * linux/drivers/char/tty_io.c:do_tty_hangup() ++ * We're called from the eventd thread, so we can sleep for ++ * a _short_ time only. ++ */ ++static void uart_hangup(struct tty_struct *tty) ++{ ++ struct uart_state *state = tty->driver_data; ++ ++ BUG_ON(!kernel_locked()); ++ DPRINTK("uart_hangup(%d)\n", state->port->line); ++ ++ down(&state->sem); ++ if (state->info && state->info->flags & (UIF_NORMAL_ACTIVE|UIF_CALLOUT_ACTIVE)) { ++ uart_flush_buffer(tty); ++ uart_shutdown(state); ++ state->count = 0; ++ state->info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CALLOUT_ACTIVE); ++ state->info->tty = NULL; ++ wake_up_interruptible(&state->info->open_wait); ++ wake_up_interruptible(&state->info->delta_msr_wait); ++ } ++ up(&state->sem); ++} ++ ++/* ++ * Copy across the serial console cflag setting into the termios settings ++ * for the initial open of the port. This allows continuity between the ++ * kernel settings, and the settings init adopts when it opens the port ++ * for the first time. ++ */ ++static void uart_update_termios(struct uart_state *state) ++{ ++ struct tty_struct *tty = state->info->tty; ++ struct uart_port *port = state->port; ++ ++ if (uart_console(port) && port->cons->cflag) { ++ tty->termios->c_cflag = port->cons->cflag; ++ port->cons->cflag = 0; ++ } ++ ++ /* ++ * If the device failed to grab its irq resources, ++ * or some other error occurred, don't try to talk ++ * to the port hardware. ++ */ ++ if (!(tty->flags & (1 << TTY_IO_ERROR))) { ++ /* ++ * Make termios settings take effect. ++ */ ++ uart_change_speed(state, NULL); ++ ++ /* ++ * And finally enable the RTS and DTR signals. ++ */ ++ if (tty->termios->c_cflag & CBAUD) ++ uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); ++ } ++} ++ ++/* ++ * Block the open until the port is ready. We must be called with ++ * the per-port semaphore held. ++ */ ++static int ++uart_block_til_ready(struct file *filp, struct uart_state *state) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ struct uart_info *info = state->info; ++ struct uart_port *port = state->port; ++ struct termios *termios; ++ ++ /* ++ * If this is a callout device, then just make sure the normal ++ * device isn't being used. ++ */ ++ if (info->tty->driver.subtype == SERIAL_TYPE_CALLOUT) { ++ if (info->flags & UIF_NORMAL_ACTIVE) ++ return -EBUSY; ++ if ((info->flags & UIF_CALLOUT_ACTIVE) && ++ (info->flags & ASYNC_SESSION_LOCKOUT) && ++ (info->session != current->session)) ++ return -EBUSY; ++ if ((info->flags & UIF_CALLOUT_ACTIVE) && ++ (info->flags & ASYNC_PGRP_LOCKOUT) && ++ (info->pgrp != current->pgrp)) ++ return -EBUSY; ++ info->flags |= UIF_CALLOUT_ACTIVE; ++ return 0; ++ } ++ ++ if (info->flags & UIF_CALLOUT_ACTIVE) { ++ termios = &state->normal_termios; ++ } else { ++ termios = state->info->tty->termios; ++ } ++ ++ info->blocked_open++; ++ state->count--; ++ ++ add_wait_queue(&info->open_wait, &wait); ++ while (1) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ /* ++ * If we have been hung up, tell userspace/restart open. ++ */ ++ if (tty_hung_up_p(filp) || info->tty == NULL) ++ break; ++ ++ /* ++ * If the port has been closed, tell userspace/restart open. ++ */ ++ if (!(info->flags & UIF_INITIALIZED)) ++ break; ++ ++ /* ++ * If non-blocking mode is set, or CLOCAL mode is set, ++ * we don't want to wait for the modem status lines to ++ * indicate that the port is ready. ++ * ++ * Also, if the port is not enabled/configured, we want ++ * to allow the open to succeed here. Note that we will ++ * have set TTY_IO_ERROR for a non-existant port. ++ */ ++ if ((filp->f_flags & O_NONBLOCK) || ++ (termios->c_cflag & CLOCAL) || ++ (info->tty->flags & (1 << TTY_IO_ERROR))) { ++ break; ++ } ++ ++ if (!(info->flags & UIF_CALLOUT_ACTIVE)) { ++ /* ++ * Set DTR to allow modem to know we're waiting. Do ++ * not set RTS here - we want to make sure we catch ++ * the data from the modem. ++ */ ++ if (info->tty->termios->c_cflag & CBAUD) ++ uart_set_mctrl(port, TIOCM_DTR); ++ ++ /* ++ * and wait for the carrier to indicate that the ++ * modem is ready for us. ++ */ ++ if (port->ops->get_mctrl(port) & TIOCM_CAR) ++ break; ++ } ++ ++ up(&state->sem); ++ schedule(); ++ down(&state->sem); ++ ++ if (signal_pending(current)) ++ break; ++ } ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&info->open_wait, &wait); ++ ++ state->count++; ++ info->blocked_open--; ++ ++ info->flags |= UIF_NORMAL_ACTIVE; ++ ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ ++ if (!info->tty || tty_hung_up_p(filp)) ++ return -EAGAIN; ++ ++ return 0; ++} ++ ++static struct uart_state *uart_get(struct uart_driver *drv, int line) ++{ ++ struct uart_state *state; ++ ++ down(&port_sem); ++ state = drv->state + line; ++ if (down_interruptible(&state->sem)) { ++ state = ERR_PTR(-ERESTARTSYS); ++ goto out; ++ } ++ ++ state->count++; ++ if (!state->port) { ++ state->count--; ++ up(&state->sem); ++ state = ERR_PTR(-ENXIO); ++ goto out; ++ } ++ ++ if (!state->info) { ++ state->info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); ++ if (state->info) { ++ memset(state->info, 0, sizeof(struct uart_info)); ++ init_waitqueue_head(&state->info->open_wait); ++ init_waitqueue_head(&state->info->delta_msr_wait); ++ ++ /* ++ * Link the info into the other structures. ++ */ ++ state->port->info = state->info; ++ ++ tasklet_init(&state->info->tlet, uart_tasklet_action, ++ (unsigned long)state); ++ } else { ++ state->count--; ++ up(&state->sem); ++ state = ERR_PTR(-ENOMEM); ++ } ++ } ++ ++ out: ++ up(&port_sem); ++ return state; ++} ++ ++/* ++ * In 2.4.5, calls to uart_open are serialised by the BKL in ++ * linux/fs/devices.c:chrdev_open() ++ * Note that if this fails, then uart_close() _will_ be called. ++ */ ++static int uart_open(struct tty_struct *tty, struct file *filp) ++{ ++ struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; ++ struct uart_state *state; ++ int retval, line = MINOR(tty->device) - tty->driver.minor_start; ++ ++ BUG_ON(!kernel_locked()); ++ DPRINTK("uart_open(%d) called\n", line); ++ ++ /* ++ * tty->driver->num won't change, so we won't fail here with ++ * tty->driver_data set to something non-NULL (and therefore ++ * we won't get caught by uart_close()). ++ */ ++ retval = -ENODEV; ++ if (line >= tty->driver.num) ++ goto fail; ++ ++ if (!try_inc_mod_count(drv->owner)) ++ goto fail; ++ ++ /* ++ * We take the semaphore inside uart_get to guarantee that we won't ++ * be re-entered while allocating the info structure, or while we ++ * request any IRQs that the driver may need. This also has the nice ++ * side-effect that it delays the action of uart_hangup, so we can ++ * guarantee that info->tty will always contain something reasonable. ++ */ ++ state = uart_get(drv, line); ++ if (IS_ERR(state)) { ++ retval = PTR_ERR(state); ++ if (!tty->driver_data) ++ goto out; ++ else ++ goto fail; ++ } ++ ++ /* ++ * Once we set tty->driver_data here, we are guaranteed that ++ * uart_close() will decrement the driver module use count. ++ * Any failures from here onwards should not touch the count. ++ */ ++ tty->driver_data = state; ++ tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; ++ tty->alt_speed = uart_get_altspeed(state->port); ++ state->info->tty = tty; ++ ++ /* ++ * If the port is in the middle of closing, bail out now. ++ */ ++ if (tty_hung_up_p(filp)) { ++ retval = -EAGAIN; ++ state->count--; ++ up(&state->sem); ++ goto fail; ++ } ++ ++ /* ++ * Make sure the device is in D0 state. ++ */ ++ if (state->count == 1) ++ uart_change_pm(state, 0); ++ ++ /* ++ * Start up the serial port. ++ */ ++ retval = uart_startup(state, 0); ++ ++ /* ++ * If we succeeded, wait until the port is ready. ++ */ ++ if (retval == 0) ++ retval = uart_block_til_ready(filp, state); ++ ++ /* ++ * If this is the first open to succeed, adjust things to suit. ++ */ ++ if (retval == 0 && state->count == 1) { ++ if (state->port->flags & UPF_SPLIT_TERMIOS) { ++ if (tty->driver.subtype == SERIAL_TYPE_NORMAL) ++ *tty->termios = state->normal_termios; ++ else ++ *tty->termios = state->callout_termios; ++ } ++ ++ uart_update_termios(state); ++ ++ state->info->session = current->session; ++ state->info->pgrp = current->pgrp; ++ } ++ up(&state->sem); ++ ++ return 0; ++ ++ out: ++ if (drv->owner) ++ __MOD_DEC_USE_COUNT(drv->owner); ++ fail: ++ return retval; ++} ++ ++static const char *uart_type(struct uart_port *port) ++{ ++ const char *str = NULL; ++ ++ if (port->ops->type) ++ str = port->ops->type(port); ++ ++ if (!str) ++ str = "unknown"; ++ ++ return str; ++} ++ ++#ifdef CONFIG_PROC_FS ++ ++static int uart_line_info(char *buf, struct uart_driver *drv, int i) ++{ ++ struct uart_state *state = drv->state + i; ++ struct uart_port *port = state->port; ++ char stat_buf[32]; ++ unsigned int status; ++ int ret; ++ ++ if (!port) ++ return 0; ++ ++ ret = sprintf(buf, "%d: uart:%s port:%08X irq:%d", ++ port->line, uart_type(port), ++ port->iobase, port->irq); ++ ++ if (port->type == PORT_UNKNOWN) { ++ strcat(buf, "\n"); ++ return ret + 1; ++ } ++ ++ status = port->ops->get_mctrl(port); ++ ++ ret += sprintf(buf + ret, " tx:%d rx:%d", ++ port->icount.tx, port->icount.rx); ++ if (port->icount.frame) ++ ret += sprintf(buf + ret, " fe:%d", ++ port->icount.frame); ++ if (port->icount.parity) ++ ret += sprintf(buf + ret, " pe:%d", ++ port->icount.parity); ++ if (port->icount.brk) ++ ret += sprintf(buf + ret, " brk:%d", ++ port->icount.brk); ++ if (port->icount.overrun) ++ ret += sprintf(buf + ret, " oe:%d", ++ port->icount.overrun); ++ ++#define INFOBIT(bit,str) \ ++ if (port->mctrl & (bit)) \ ++ strcat(stat_buf, (str)) ++#define STATBIT(bit,str) \ ++ if (status & (bit)) \ ++ strcat(stat_buf, (str)) ++ ++ stat_buf[0] = '\0'; ++ stat_buf[1] = '\0'; ++ INFOBIT(TIOCM_RTS, "|RTS"); ++ STATBIT(TIOCM_CTS, "|CTS"); ++ INFOBIT(TIOCM_DTR, "|DTR"); ++ STATBIT(TIOCM_DSR, "|DSR"); ++ STATBIT(TIOCM_CAR, "|CD"); ++ STATBIT(TIOCM_RNG, "|RI"); ++ if (stat_buf[0]) ++ stat_buf[0] = ' '; ++ strcat(stat_buf, "\n"); ++ ++ ret += sprintf(buf + ret, stat_buf); ++ return ret; ++} ++ ++static int uart_read_proc(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ struct tty_driver *ttydrv = data; ++ struct uart_driver *drv = ttydrv->driver_state; ++ int i, len = 0, l; ++ off_t begin = 0; ++ ++ len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n", ++ "", "", ""); ++ for (i = 0; i < drv->nr && len < PAGE_SIZE - 96; i++) { ++ l = uart_line_info(page + len, drv, i); ++ len += l; ++ if (len + begin > off + count) ++ goto done; ++ if (len + begin < off) { ++ begin += len; ++ len = 0; ++ } ++ } ++ *eof = 1; ++ done: ++ if (off >= len + begin) ++ return 0; ++ *start = page + (off - begin); ++ return (count < begin + len - off) ? count : (begin + len - off); ++} ++#endif ++ ++#ifdef CONFIG_SERIAL_CORE_CONSOLE ++/* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++struct uart_port * __init ++uart_get_console(struct uart_port *ports, int nr, struct console *co) ++{ ++ int idx = co->index; ++ ++ if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 && ++ ports[idx].membase == NULL)) ++ for (idx = 0; idx < nr; idx++) ++ if (ports[idx].iobase != 0 || ++ ports[idx].membase != NULL) ++ break; ++ ++ co->index = idx; ++ ++ return ports + idx; ++} ++ ++/** ++ * uart_parse_options - Parse serial port baud/parity/bits/flow contro. ++ * @options: pointer to option string ++ * @baud: pointer to an 'int' variable for the baud rate. ++ * @parity: pointer to an 'int' variable for the parity. ++ * @bits: pointer to an 'int' variable for the number of data bits. ++ * @flow: pointer to an 'int' variable for the flow control character. ++ * ++ * uart_parse_options decodes a string containing the serial console ++ * options. The format of the string is , ++ * eg: 115200n8r ++ */ ++void __init ++uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow) ++{ ++ char *s = options; ++ ++ *baud = simple_strtoul(s, NULL, 10); ++ while (*s >= '0' && *s <= '9') ++ s++; ++ if (*s) ++ *parity = *s++; ++ if (*s) ++ *bits = *s++ - '0'; ++ if (*s) ++ *flow = *s; ++} ++ ++struct baud_rates { ++ unsigned int rate; ++ unsigned int cflag; ++}; ++ ++static struct baud_rates baud_rates[] = { ++ { 921600, B921600 }, ++ { 460800, B460800 }, ++ { 230400, B230400 }, ++ { 115200, B115200 }, ++ { 57600, B57600 }, ++ { 38400, B38400 }, ++ { 19200, B19200 }, ++ { 9600, B9600 }, ++ { 4800, B4800 }, ++ { 2400, B2400 }, ++ { 1200, B1200 }, ++ { 0, B38400 } ++}; ++ ++/** ++ * uart_set_options - setup the serial console parameters ++ * @port: pointer to the serial ports uart_port structure ++ * @co: console pointer ++ * @baud: baud rate ++ * @parity: parity character - 'n' (none), 'o' (odd), 'e' (even) ++ * @bits: number of data bits ++ * @flow: flow control character - 'r' (rts) ++ */ ++int __init ++uart_set_options(struct uart_port *port, struct console *co, ++ int baud, int parity, int bits, int flow) ++{ ++ struct termios termios; ++ unsigned int quot; ++ int i; ++ ++ memset(&termios, 0, sizeof(struct termios)); ++ ++ termios.c_cflag = CREAD | HUPCL | CLOCAL; ++ ++ /* ++ * Construct a cflag setting. ++ */ ++ for (i = 0; baud_rates[i].rate; i++) ++ if (baud_rates[i].rate <= baud) ++ break; ++ ++ termios.c_cflag |= baud_rates[i].cflag; ++ baud = baud_rates[i].rate; ++ if (baud == 0) ++ baud = 38400; ++ ++ if (bits == 7) ++ termios.c_cflag |= CS7; ++ else ++ termios.c_cflag |= CS8; ++ ++ switch (parity) { ++ case 'o': case 'O': ++ termios.c_cflag |= PARODD; ++ /*fall through*/ ++ case 'e': case 'E': ++ termios.c_cflag |= PARENB; ++ break; ++ } ++ ++ if (flow == 'r') ++ termios.c_cflag |= CRTSCTS; ++ ++ quot = (port->uartclk / (16 * baud)); ++ port->ops->change_speed(port, termios.c_cflag, 0, quot); ++ co->cflag = termios.c_cflag; ++ ++ return 0; ++} ++ ++extern void ambauart_console_init(void); ++extern void anakin_console_init(void); ++extern void clps711xuart_console_init(void); ++extern void sa1100_rs_console_init(void); ++extern void serial8250_console_init(void); ++extern void at91_console_init(void); ++ ++/* ++ * Central "initialise all serial consoles" container. Needs to be killed. ++ */ ++void __init uart_console_init(void) ++{ ++#ifdef CONFIG_SERIAL_AMBA_CONSOLE ++ ambauart_console_init(); ++#endif ++#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE ++ anakin_console_init(); ++#endif ++#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE ++ clps711xuart_console_init(); ++#endif ++#ifdef CONFIG_SERIAL_SA1100_CONSOLE ++ sa1100_rs_console_init(); ++#endif ++#ifdef CONFIG_SERIAL_AT91_CONSOLE ++ at91_console_init(); ++#endif ++#ifdef CONFIG_SERIAL_8250_CONSOLE ++ serial8250_console_init(); ++#endif ++#ifdef CONFIG_SERIAL_UART00_CONSOLE ++ uart00_console_init(); ++#endif ++} ++#endif /* CONFIG_SERIAL_CORE_CONSOLE */ ++ ++static void uart_change_pm(struct uart_state *state, int pm_state) ++{ ++ struct uart_port *port = state->port; ++ if (port->ops->pm) ++ port->ops->pm(port, pm_state, 0); ++} ++ ++#ifdef CONFIG_PM ++int uart_suspend_port(struct uart_state *state) ++{ ++ struct uart_port *port = state->port; ++ ++ down(&state->sem); ++ if (port) { ++ /* ++ * Disable the console device before suspending. ++ */ ++ if (uart_console(port)) ++ port->cons->flags &= ~CON_ENABLED; ++ ++ if (state->info && state->info->flags & UIF_INITIALIZED) { ++ struct uart_ops *ops = port->ops; ++ ++ spin_lock_irq(&port->lock); ++ ops->stop_tx(port, 0); ++ ops->set_mctrl(port, 0); ++ ops->stop_rx(port); ++ spin_unlock_irq(&port->lock); ++ ++ /* ++ * Wait for the transmitter to empty. ++ */ ++ while (!ops->tx_empty(port)) { ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(10*HZ/1000); ++ } ++ set_current_state(TASK_RUNNING); ++ ++ ops->shutdown(port); ++ } ++ ++ uart_change_pm(state, 3); ++ } ++ ++ up(&state->sem); ++ ++ return 0; ++} ++ ++int uart_resume_port(struct uart_state *state) ++{ ++ struct uart_port *port = state->port; ++ ++ down(&state->sem); ++ if (port) { ++ uart_change_pm(state, 0); ++ ++ /* ++ * Re-enable the console device after suspending. ++ */ ++ if (uart_console(port)) { ++ uart_change_speed(state, NULL); ++ port->cons->flags |= CON_ENABLED; ++ } ++ ++ if (state->info && state->info->flags & UIF_INITIALIZED) { ++ struct uart_ops *ops = port->ops; ++ ++ ops->set_mctrl(port, 0); ++ ops->startup(port); ++ uart_change_speed(state, NULL); ++ spin_lock_irq(&port->lock); ++ ops->set_mctrl(port, port->mctrl); ++ ops->start_tx(port, 0); ++ spin_unlock_irq(&port->lock); ++ } ++ } ++ ++ up(&state->sem); ++ ++ return 0; ++} ++ ++/* ++ * Wakeup support. ++ */ ++static int uart_pm_set_wakeup(struct uart_state *state, int data) ++{ ++ int err = 0; ++ ++ if (state->port->ops->set_wake) ++ err = state->port->ops->set_wake(state->port, data); ++ ++ return err; ++} ++ ++static int uart_pm(struct pm_dev *dev, pm_request_t rqst, void *data) ++{ ++ struct uart_state *state = dev->data; ++ int err = 0; ++ ++ if (state->port && state->port->type == PORT_UNKNOWN) ++ return 0; ++ ++ switch (rqst) { ++ case PM_SUSPEND: ++ err = uart_suspend_port(state); ++ break; ++ ++ case PM_RESUME: ++ err = uart_resume_port(state); ++ break; ++ ++ case PM_SET_WAKEUP: ++ err = uart_pm_set_wakeup(state, (int)data); ++ break; ++ } ++ return err; ++} ++#endif ++ ++static inline void ++uart_report_port(struct uart_driver *drv, struct uart_port *port) ++{ ++ printk("%s%d at ", drv->normal_name, port->line); ++ switch (port->iotype) { ++ case UPIO_PORT: ++ printk("I/O 0x%x", port->iobase); ++ break; ++ case UPIO_HUB6: ++ printk("I/O 0x%x offset 0x%x", port->iobase, port->hub6); ++ break; ++ case UPIO_MEM: ++ printk("MMIO 0x%lx", port->mapbase); ++ break; ++ } ++ printk(" (irq = %d) is a %s\n", port->irq, uart_type(port)); ++} ++ ++static void ++uart_configure_port(struct uart_driver *drv, struct uart_state *state, ++ struct uart_port *port) ++{ ++ unsigned int flags; ++ ++ /* ++ * If there isn't a port here, don't do anything further. ++ */ ++ if (!port->iobase && !port->mapbase && !port->membase) ++ return; ++ ++ /* ++ * Now do the auto configuration stuff. Note that config_port ++ * is expected to claim the resources and map the port for us. ++ */ ++ flags = UART_CONFIG_TYPE; ++ if (port->flags & UPF_AUTO_IRQ) ++ flags |= UART_CONFIG_IRQ; ++ if (port->flags & UPF_BOOT_AUTOCONF) { ++ port->type = PORT_UNKNOWN; ++ port->ops->config_port(port, flags); ++ } ++ ++ if (port->type != PORT_UNKNOWN) { ++ unsigned long flags; ++ ++ uart_report_port(drv, port); ++ ++ /* ++ * Ensure that the modem control lines are de-activated. ++ * We probably don't need a spinlock around this, but ++ */ ++ spin_lock_irqsave(&port->lock, flags); ++ port->ops->set_mctrl(port, 0); ++ spin_unlock_irqrestore(&port->lock, flags); ++ ++ /* ++ * Power down all ports by default, except the ++ * console if we have one. ++ */ ++ if (!uart_console(port)) ++ uart_change_pm(state, 3); ++ } ++} ++ ++/* ++ * This reverses the effects of uart_configure_port, hanging up the ++ * port before removal. ++ */ ++static void ++uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state) ++{ ++ struct uart_port *port = state->port; ++ struct uart_info *info = state->info; ++ ++ if (info && info->tty) ++ tty_vhangup(info->tty); ++ ++ down(&state->sem); ++ ++ state->info = NULL; ++ ++ /* ++ * Free the port IO and memory resources, if any. ++ */ ++ if (port->type != PORT_UNKNOWN) ++ port->ops->release_port(port); ++ ++ /* ++ * Indicate that there isn't a port here anymore. ++ */ ++ port->type = PORT_UNKNOWN; ++ ++ /* ++ * Kill the tasklet, and free resources. ++ */ ++ if (info) { ++ tasklet_kill(&info->tlet); ++ kfree(info); ++ } ++ ++ up(&state->sem); ++} ++ ++/** ++ * uart_register_driver - register a driver with the uart core layer ++ * @drv: low level driver structure ++ * ++ * Register a uart driver with the core driver. We in turn register ++ * with the tty layer, and initialise the core driver per-port state. ++ * ++ * We have a proc file in /proc/tty/driver which is named after the ++ * normal driver. ++ * ++ * drv->port should be NULL, and the per-port structures should be ++ * registered using uart_add_one_port after this call has succeeded. ++ */ ++int uart_register_driver(struct uart_driver *drv) ++{ ++ struct tty_driver *normal, *callout; ++ int i, retval; ++ ++ BUG_ON(drv->state); ++ ++ /* ++ * Maybe we should be using a slab cache for this, especially if ++ * we have a large number of ports to handle. Note that we also ++ * allocate space for an integer for reference counting. ++ */ ++ drv->state = kmalloc(sizeof(struct uart_state) * drv->nr + ++ sizeof(int), GFP_KERNEL); ++ retval = -ENOMEM; ++ if (!drv->state) ++ goto out; ++ ++ memset(drv->state, 0, sizeof(struct uart_state) * drv->nr + ++ sizeof(int)); ++ ++ normal = drv->normal_driver; ++ callout = drv->callout_driver; ++ ++ normal->magic = TTY_DRIVER_MAGIC; ++ normal->driver_name = drv->normal_name; ++ normal->name = drv->normal_name; ++ normal->major = drv->normal_major; ++ normal->minor_start = drv->minor; ++ normal->num = drv->nr; ++ normal->type = TTY_DRIVER_TYPE_SERIAL; ++ normal->subtype = SERIAL_TYPE_NORMAL; ++ normal->init_termios = tty_std_termios; ++ normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; ++ normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; ++ normal->refcount = (int *)(drv->state + drv->nr); ++ normal->table = drv->table; ++ normal->termios = drv->termios; ++ normal->termios_locked = drv->termios_locked; ++ normal->driver_state = drv; ++ ++ normal->open = uart_open; ++ normal->close = uart_close; ++ normal->write = uart_write; ++ normal->put_char = uart_put_char; ++ normal->flush_chars = uart_flush_chars; ++ normal->write_room = uart_write_room; ++ normal->chars_in_buffer = uart_chars_in_buffer; ++ normal->flush_buffer = uart_flush_buffer; ++ normal->ioctl = uart_ioctl; ++ normal->throttle = uart_throttle; ++ normal->unthrottle = uart_unthrottle; ++ normal->send_xchar = uart_send_xchar; ++ normal->set_termios = uart_set_termios; ++ normal->stop = uart_stop; ++ normal->start = uart_start; ++ normal->hangup = uart_hangup; ++ normal->break_ctl = uart_break_ctl; ++ normal->wait_until_sent = uart_wait_until_sent; ++#ifdef CONFIG_PROC_FS ++ normal->read_proc = uart_read_proc; ++#endif ++ ++ /* ++ * The callout device is just like the normal device except for ++ * the major number and the subtype code. ++ */ ++ *callout = *normal; ++ callout->name = drv->callout_name; ++ callout->major = drv->callout_major; ++ callout->subtype = SERIAL_TYPE_CALLOUT; ++ callout->read_proc = NULL; ++ callout->proc_entry = NULL; ++ ++ /* ++ * Initialise the UART state(s). ++ */ ++ for (i = 0; i < drv->nr; i++) { ++ struct uart_state *state = drv->state + i; ++ ++ state->callout_termios = callout->init_termios; ++ state->normal_termios = normal->init_termios; ++ state->close_delay = 5 * HZ / 10; ++ state->closing_wait = 30 * HZ; ++ ++ init_MUTEX(&state->sem); ++ } ++ ++ retval = tty_register_driver(normal); ++ if (retval) ++ goto out; ++ ++ retval = tty_register_driver(callout); ++ if (retval) ++ tty_unregister_driver(normal); ++ ++ out: ++ if (retval < 0) { ++ kfree(drv->state); ++ } ++ return retval; ++} ++ ++/** ++ * uart_unregister_driver - remove a driver from the uart core layer ++ * @drv: low level driver structure ++ * ++ * Remove all references to a driver from the core driver. The low ++ * level driver must have removed all its ports via the ++ * uart_remove_one_port() if it registered them with uart_add_one_port(). ++ * (ie, drv->port == NULL) ++ */ ++void uart_unregister_driver(struct uart_driver *drv) ++{ ++ tty_unregister_driver(drv->normal_driver); ++ tty_unregister_driver(drv->callout_driver); ++ ++ kfree(drv->state); ++ drv->state = NULL; ++} ++ ++/** ++ * uart_add_one_port - attach a driver-defined port structure ++ * @drv: pointer to the uart low level driver structure for this port ++ * @port: uart port structure to use for this port. ++ * ++ * This allows the driver to register its own uart_port structure ++ * with the core driver. The main purpose is to allow the low ++ * level uart drivers to expand uart_port, rather than having yet ++ * more levels of structures. ++ */ ++int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) ++{ ++ struct uart_state *state; ++ int ret = 0; ++ ++ BUG_ON(in_interrupt()); ++ ++ if (port->line >= drv->nr) ++ return -EINVAL; ++ ++ state = drv->state + port->line; ++ ++ down(&port_sem); ++ if (state->port) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ state->port = port; ++ ++ spin_lock_init(&port->lock); ++ port->cons = drv->cons; ++ port->info = state->info; ++ ++ uart_configure_port(drv, state, port); ++ ++ /* ++ * Register the port whether it's detected or not. This allows ++ * setserial to be used to alter this ports parameters. ++ */ ++ tty_register_devfs(drv->normal_driver, 0, drv->minor + port->line); ++ tty_register_devfs(drv->callout_driver, 0, drv->minor + port->line); ++ ++#ifdef CONFIG_PM ++ port->cons = drv->cons; ++ state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm); ++ if (state->pm) ++ state->pm->data = state; ++#endif ++ ++ out: ++ up(&port_sem); ++ ++ return ret; ++} ++ ++/** ++ * uart_remove_one_port - detach a driver defined port structure ++ * @drv: pointer to the uart low level driver structure for this port ++ * @port: uart port structure for this port ++ * ++ * This unhooks (and hangs up) the specified port structure from the ++ * core driver. No further calls will be made to the low-level code ++ * for this port. ++ */ ++int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) ++{ ++ struct uart_state *state = drv->state + port->line; ++ ++ BUG_ON(in_interrupt()); ++ ++ if (state->port != port) ++ printk(KERN_ALERT "Removing wrong port: %p != %p\n", ++ state->port, port); ++ ++ down(&port_sem); ++ ++ pm_unregister(state->pm); ++ ++ /* ++ * Remove the devices from devfs ++ */ ++ tty_unregister_devfs(drv->normal_driver, drv->minor + port->line); ++ tty_unregister_devfs(drv->callout_driver, drv->minor + port->line); ++ ++ uart_unconfigure_port(drv, state); ++ state->port = NULL; ++ up(&port_sem); ++ ++ return 0; ++} ++ ++/* ++ * Are the two ports equivalent? ++ */ ++static int uart_match_port(struct uart_port *port1, struct uart_port *port2) ++{ ++ if (port1->iotype != port2->iotype) ++ return 0; ++ ++ switch (port1->iotype) { ++ case UPIO_PORT: ++ return (port1->iobase == port2->iobase); ++ case UPIO_HUB6: ++ return (port1->iobase == port2->iobase) && ++ (port1->hub6 == port2->hub6); ++ case UPIO_MEM: ++ return (port1->membase == port2->membase); ++ } ++ return 0; ++} ++ ++/* ++ * Try to find an unused uart_state slot for a port. ++ */ ++static struct uart_state * ++uart_find_match_or_unused(struct uart_driver *drv, struct uart_port *port) ++{ ++ int i; ++ ++ /* ++ * First, find a port entry which matches. Note: if we do ++ * find a matching entry, and it has a non-zero use count, ++ * then we can't register the port. ++ */ ++ for (i = 0; i < drv->nr; i++) ++ if (uart_match_port(drv->state[i].port, port)) ++ return &drv->state[i]; ++ ++ /* ++ * We didn't find a matching entry, so look for the first ++ * free entry. We look for one which hasn't been previously ++ * used (indicated by zero iobase). ++ */ ++ for (i = 0; i < drv->nr; i++) ++ if (drv->state[i].port->type == PORT_UNKNOWN && ++ drv->state[i].port->iobase == 0 && ++ drv->state[i].count == 0) ++ return &drv->state[i]; ++ ++ /* ++ * That also failed. Last resort is to find any currently ++ * entry which doesn't have a real port associated with it. ++ */ ++ for (i = 0; i < drv->nr; i++) ++ if (drv->state[i].port->type == PORT_UNKNOWN && ++ drv->state[i].count == 0) ++ return &drv->state[i]; ++ ++ return NULL; ++} ++ ++/** ++ * uart_register_port: register uart settings with a port ++ * @drv: pointer to the uart low level driver structure for this port ++ * @port: uart port structure describing the port ++ * ++ * Register UART settings with the specified low level driver. Detect ++ * the type of the port if UPF_BOOT_AUTOCONF is set, and detect the ++ * IRQ if UPF_AUTO_IRQ is set. ++ * ++ * We try to pick the same port for the same IO base address, so that ++ * when a modem is plugged in, unplugged and plugged back in, it gets ++ * allocated the same port. ++ * ++ * Returns negative error, or positive line number. ++ */ ++int uart_register_port(struct uart_driver *drv, struct uart_port *port) ++{ ++ struct uart_state *state; ++ int ret; ++ ++ down(&port_sem); ++ ++ state = uart_find_match_or_unused(drv, port); ++ ++ if (state) { ++ /* ++ * Ok, we've found a line that we can use. ++ * ++ * If we find a port that matches this one, and it appears ++ * to be in-use (even if it doesn't have a type) we shouldn't ++ * alter it underneath itself - the port may be open and ++ * trying to do useful work. ++ */ ++ if (uart_users(state) != 0) { ++ ret = -EBUSY; ++ goto out; ++ } ++ ++ /* ++ * If the port is already initialised, don't touch it. ++ */ ++ if (state->port->type == PORT_UNKNOWN) { ++ state->port->iobase = port->iobase; ++ state->port->membase = port->membase; ++ state->port->irq = port->irq; ++ state->port->uartclk = port->uartclk; ++ state->port->fifosize = port->fifosize; ++ state->port->regshift = port->regshift; ++ state->port->iotype = port->iotype; ++ state->port->flags = port->flags; ++ state->port->line = state - drv->state; ++ state->port->mapbase = port->mapbase; ++ ++ uart_configure_port(drv, state, state->port); ++ } ++ ++ ret = state->port->line; ++ } else ++ ret = -ENOSPC; ++ out: ++ up(&port_sem); ++ return ret; ++} ++ ++/** ++ * uart_unregister_port - de-allocate a port ++ * @drv: pointer to the uart low level driver structure for this port ++ * @line: line index previously returned from uart_register_port() ++ * ++ * Hang up the specified line associated with the low level driver, ++ * and mark the port as unused. ++ */ ++void uart_unregister_port(struct uart_driver *drv, int line) ++{ ++ struct uart_state *state; ++ ++ if (line < 0 || line >= drv->nr) { ++ printk(KERN_ERR "Attempt to unregister %s%d\n", ++ drv->normal_name, line); ++ return; ++ } ++ ++ state = drv->state + line; ++ ++ down(&port_sem); ++ uart_unconfigure_port(drv, state); ++ up(&port_sem); ++} ++ ++EXPORT_SYMBOL(uart_write_wakeup); ++EXPORT_SYMBOL(uart_register_driver); ++EXPORT_SYMBOL(uart_unregister_driver); ++EXPORT_SYMBOL(uart_register_port); ++EXPORT_SYMBOL(uart_unregister_port); ++EXPORT_SYMBOL(uart_add_one_port); ++EXPORT_SYMBOL(uart_remove_one_port); ++ ++MODULE_DESCRIPTION("Serial driver core"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/serial/omaha.c linux-2.4.26-vrs1/drivers/serial/omaha.c +--- linux-2.4.26/drivers/serial/omaha.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/omaha.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,584 @@ ++/* ++ * linux/drivers/char/omaha.c ++ * ++ * Driver for Omaha serial port ++ * ++ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. ++ * ++ * Copyright 1999-2002 ARM Limited ++ * Copyright (C) 2000 Deep Blue Solutions Ltd. ++ * ++ * 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 ++ * ++ * $Id: serial_amba.c,v 1.4 2001/07/17 20:34:27 rmk Exp $ ++ * ++ * This is a generic driver for ARM AMBA-type serial ports. They ++ * have a lot of 16550-like features, but are not register compatable. ++ * Note that although they do have CTS, DCD and DSR inputs, they do ++ * not have an RI input, nor do they have DTR or RTS outputs. If ++ * required, these have to be supplied via some other means (eg, GPIO) ++ * and hooked into this driver. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#if defined(CONFIG_SERIAL_OMAHA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ++#define SUPPORT_SYSRQ ++#endif ++ ++#include ++ ++#include ++ ++#define UART_NR 1 ++ ++#define SERIAL_OMAHA_MAJOR 204 ++#define SERIAL_OMAHA_MINOR 32 ++#define SERIAL_OMAHA_NR UART_NR ++ ++#define CALLOUT_OMAHA_NAME "cuaom" ++#define CALLOUT_OMAHA_MAJOR 205 ++#define CALLOUT_OMAHA_MINOR 32 ++#define CALLOUT_OMAHA_NR UART_NR ++ ++static struct tty_driver normal, callout; ++static struct tty_struct *omaha_table[UART_NR]; ++static struct termios *omaha_termios[UART_NR], *omaha_termios_locked[UART_NR]; ++#ifdef SUPPORT_SYSRQ ++static struct console omaha_console; ++#endif ++ ++#define OMAHA_ISR_PASS_LIMIT 256 ++ ++/* ++ * Access macros for the Omaha UARTs ++ */ ++ ++#define UART_GET_FR(p) readb((p)->membase + OMAHA_UTRSTAT) ++#define UART_GET_CHAR(p) readb((p)->membase + OMAHA_URXH) ++#define UART_PUT_CHAR(p, c) writel((c), (p)->membase + OMAHA_UTXH) ++#define UART_GET_RSR(p) readb((p)->membase + OMAHA_UERSTAT) ++#define UART_FIFO_STATUS(p) (readl((p)->membase + OMAHA_UFSTAT)) ++#define UART_RX_DATA(s) (((s) & OMAHA_RXFF_CNT) != 0) ++#define UART_TX_DATA(s) (!((s) & OMAHA_TXFF)) ++#define UART_TX_READY(s) (((s) & OMAHA_UTX_EMPTY)) ++#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & OMAHA_UTXEMPTY) != 0) ++ ++#define UART_DUMMY_RSR_RX 256 ++#define UART_PORT_SIZE 64 ++ ++#define RX_IRQ(port) ((port)->irq) ++#define TX_IRQ(port) ((port)->irq + 5) ++ ++/* ++ * Our private driver data mappings. ++ */ ++#define drv_old_status driver_priv ++ ++static void omahauart_stop_tx(struct uart_port *port, u_int from_tty) ++{ ++ disable_irq(TX_IRQ(port)); ++} ++ ++static void omahauart_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty) ++{ ++ if (nonempty) ++ enable_irq(TX_IRQ(port)); ++} ++ ++static void omahauart_stop_rx(struct uart_port *port) ++{ ++ disable_irq(RX_IRQ(port)); ++} ++ ++static void omahauart_enable_ms(struct uart_port *port) ++{ ++ // Do nothing... ++} ++ ++static void ++#ifdef SUPPORT_SYSRQ ++omahauart_rx_chars(struct uart_info *info, struct pt_regs *regs) ++#else ++omahauart_rx_chars(struct uart_info *info) ++#endif ++{ ++ struct tty_struct *tty = info->tty; ++ volatile unsigned int status, data, ch, rsr, max_count = 256; ++ struct uart_port *port = info->port; ++ ++ status = UART_FIFO_STATUS(port); ++ while (UART_RX_DATA(status) && max_count--) { ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) { ++ tty->flip.tqueue.routine((void *)tty); ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) { ++ printk(KERN_WARNING "TTY_DONT_FLIP set\n"); ++ return; ++ } ++ } ++ ++ ch = UART_GET_CHAR(port); ++ ++ *tty->flip.char_buf_ptr = ch; ++ *tty->flip.flag_buf_ptr = TTY_NORMAL; ++ port->icount.rx++; ++ ++ /* ++ * Note that the error handling code is ++ * out of the main execution path ++ */ ++ rsr = UART_GET_RSR(port) | UART_DUMMY_RSR_RX; ++ if (rsr & 0xf) { ++ if (rsr & OMAHA_UART_BREAK) { ++ rsr &= ~(OMAHA_UART_FRAME | OMAHA_UART_PARITY); ++ port->icount.brk++; ++ if (uart_handle_break(info, &omaha_console)) ++ goto ignore_char; ++ } else if (rsr & OMAHA_UART_PARITY) ++ port->icount.parity++; ++ else if (rsr & OMAHA_UART_FRAME) ++ port->icount.frame++; ++ if (rsr & OMAHA_UART_OVERRUN) ++ port->icount.overrun++; ++ ++ rsr &= port->read_status_mask; ++ ++ if (rsr & OMAHA_UART_BREAK) ++ *tty->flip.flag_buf_ptr = TTY_BREAK; ++ else if (rsr & OMAHA_UART_PARITY) ++ *tty->flip.flag_buf_ptr = TTY_PARITY; ++ else if (rsr & OMAHA_UART_FRAME) ++ *tty->flip.flag_buf_ptr = TTY_FRAME; ++ } ++ ++ if (uart_handle_sysrq_char(info, ch, regs)) ++ goto ignore_char; ++ ++ if ((rsr & port->ignore_status_mask) == 0) { ++ tty->flip.flag_buf_ptr++; ++ tty->flip.char_buf_ptr++; ++ tty->flip.count++; ++ } ++ if ((rsr & OMAHA_UART_OVERRUN) && ++ tty->flip.count < TTY_FLIPBUF_SIZE) { ++ /* ++ * Overrun is special, since it's reported ++ * immediately, and doesn't affect the current ++ * character ++ */ ++ *tty->flip.char_buf_ptr++ = 0; ++ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; ++ tty->flip.count++; ++ } ++ ignore_char: ++ status = UART_FIFO_STATUS(port); ++ } ++ tty_flip_buffer_push(tty); ++ return; ++} ++ ++static void omahauart_tx_chars(struct uart_info *info) ++{ ++ struct uart_port *port = info->port; ++ volatile unsigned int status; ++ ++ if (port->x_char) { ++ UART_PUT_CHAR(port, port->x_char); ++ port->icount.tx++; ++ port->x_char = 0; ++ return; ++ } ++ if (info->xmit.head == info->xmit.tail ++ || info->tty->stopped ++ || info->tty->hw_stopped) { ++ omahauart_stop_tx(port, 0); ++ return; ++ } ++ ++ status = UART_FIFO_STATUS(info->port); ++ ++ // FIll FIFO as far as possible ++ while(UART_TX_DATA(UART_FIFO_STATUS(info->port))) ++ { ++ UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]); ++ info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1); ++ port->icount.tx++; ++ if (info->xmit.head == info->xmit.tail) ++ break; ++ } ++ ++ if (CIRC_CNT(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE) < ++ WAKEUP_CHARS) ++ uart_event(info, EVT_WRITE_WAKEUP); ++ ++ if (info->xmit.head == info->xmit.tail) ++ omahauart_stop_tx(info->port, 0); ++} ++ ++static void omahauart_int_tx(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct uart_info *info = dev_id; ++ volatile unsigned int status, pass_counter = OMAHA_ISR_PASS_LIMIT; ++ ++ status = UART_FIFO_STATUS(info->port); ++ do { ++ // TX if FIFO not full ++ if (UART_TX_DATA(status)) ++ omahauart_tx_chars(info); ++ ++ if (pass_counter-- == 0) ++ break; ++ ++ status = UART_FIFO_STATUS(info->port); ++ } while (UART_TX_DATA(status)); ++} ++ ++static void omahauart_int_rx(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct uart_info *info = dev_id; ++ volatile unsigned int status, pass_counter = OMAHA_ISR_PASS_LIMIT; ++ ++ status = UART_FIFO_STATUS(info->port); ++ do { ++ if (UART_RX_DATA(status)) ++#ifdef SUPPORT_SYSRQ ++ omahauart_rx_chars(info, regs); ++#else ++ omahauart_rx_chars(info); ++#endif ++ ++ if (pass_counter-- == 0) ++ break; ++ ++ status = UART_FIFO_STATUS(info->port); ++ } while (UART_RX_DATA(status)); ++} ++ ++static u_int omahauart_tx_empty(struct uart_port *port) ++{ ++ return UART_FIFO_STATUS(port) ? 0 : TIOCSER_TEMT; ++} ++ ++static int omahauart_get_mctrl(struct uart_port *port) ++{ ++ // Report no errors. ++ ++ return 0; ++} ++ ++static void omahauart_set_mctrl(struct uart_port *port, u_int mctrl) ++{ ++ // Do nothing. ++} ++ ++static void omahauart_break_ctl(struct uart_port *port, int break_state) ++{ ++ // Do nothing. ++} ++ ++static int omahauart_startup(struct uart_port *port, struct uart_info *info) ++{ ++ unsigned int tmp; ++ int retval; ++ ++ /* ++ * Allocate the IRQs ++ */ ++ retval = request_irq(TX_IRQ(port), omahauart_int_tx, 0, "omaha_uart_tx", info); ++ if (retval) ++ return retval; ++ ++ retval = request_irq(RX_IRQ(port), omahauart_int_rx, 0, "omaha_uart_rx", info); ++ ++ if (retval) ++ { ++ free_irq(TX_IRQ(port), info); ++ return retval; ++ } ++ ++ /* ++ * initialise the old status of the modem signals ++ */ ++ info->drv_old_status = 0; ++ ++ // Clear all errors ++ writel(0, port->membase + OMAHA_UERSTAT); ++ ++ // Enable FIFO, 16-byte watermark, also do reset (auto-clearing) ++ writel(0xF7, port->membase + OMAHA_UFCON); ++ ++ // Level driven TX/RX ints, with rx timeout enabled ++ tmp = readl(port->membase + OMAHA_UCON); ++ tmp |= 0x280; // rx is pulse driven... ++ writel(tmp, port->membase + OMAHA_UCON); ++ ++ return 0; ++} ++ ++static void omahauart_shutdown(struct uart_port *port, struct uart_info *info) ++{ ++ /* ++ * Free the interrupt ++ */ ++ free_irq(TX_IRQ(port), info); /* TX interrupt */ ++ free_irq(RX_IRQ(port), info); /* RX interrupt */ ++ ++} ++ ++static void omahauart_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot) ++{ ++ // Do nothing. ++} ++ ++static const char *omahauart_type(struct uart_port *port) ++{ ++ return port->type == PORT_OMAHA ? "OMAHA" : NULL; ++} ++ ++/* ++ * Release the memory region(s) being used by 'port' ++ */ ++static void omahauart_release_port(struct uart_port *port) ++{ ++ release_mem_region(port->mapbase, UART_PORT_SIZE); ++} ++ ++/* ++ * Request the memory region(s) being used by 'port' ++ */ ++static int omahauart_request_port(struct uart_port *port) ++{ ++ return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_omaha") ++ != NULL ? 0 : -EBUSY; ++} ++ ++/* ++ * Configure/autoconfigure the port. ++ */ ++static void omahauart_config_port(struct uart_port *port, int flags) ++{ ++ if (flags & UART_CONFIG_TYPE) { ++ port->type = PORT_OMAHA; ++ omahauart_request_port(port); ++ } ++} ++ ++/* ++ * verify the new serial_struct (for TIOCSSERIAL). ++ */ ++static int omahauart_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ int ret = 0; ++ if (ser->type != PORT_UNKNOWN && ser->type != PORT_OMAHA) ++ ret = -EINVAL; ++ if (ser->irq < 0 || ser->irq >= NR_IRQS) ++ ret = -EINVAL; ++ if (ser->baud_base < 9600) ++ ret = -EINVAL; ++ return ret; ++} ++ ++static struct uart_ops omaha_pops = { ++ .tx_empty = omahauart_tx_empty, ++ .set_mctrl = omahauart_set_mctrl, ++ .get_mctrl = omahauart_get_mctrl, ++ .stop_tx = omahauart_stop_tx, ++ .start_tx = omahauart_start_tx, ++ .stop_rx = omahauart_stop_rx, ++ .enable_ms = omahauart_enable_ms, ++ .break_ctl = omahauart_break_ctl, ++ .startup = omahauart_startup, ++ .shutdown = omahauart_shutdown, ++ .change_speed = omahauart_change_speed, ++ .type = omahauart_type, ++ .release_port = omahauart_release_port, ++ .request_port = omahauart_request_port, ++ .config_port = omahauart_config_port, ++ .verify_port = omahauart_verify_port, ++}; ++ ++static struct uart_port omaha_ports[UART_NR] = { ++ { ++ .membase = (void *)IO_ADDRESS(OMAHA_UART0_BASE), ++ .mapbase = OMAHA_UART0_BASE, ++ .iotype = SERIAL_IO_MEM, ++ .irq = OMAHA_INT_URXD0, ++ .uartclk = 10000000, ++ .fifosize = 8, ++ .unused = { 4, 5 }, /*Udriver_priv: PORT_CTRLS(5, 4), */ ++ .ops = &omaha_pops, ++ .flags = ASYNC_BOOT_AUTOCONF, ++ } ++}; ++ ++#ifdef CONFIG_SERIAL_OMAHA_CONSOLE ++static void omahauart_console_write(struct console *co, const char *s, u_int count) ++{ ++ struct uart_port *port = omaha_ports + co->index; ++ unsigned int status; ++ int i; ++ ++ /* ++ * First save the CR then disable the interrupts ++ */ ++ ++ /* ++ * Now, do each character ++ */ ++ for (i = 0; i < count; i++) { ++ do { ++ status = UART_GET_FR(port); ++ } while ((status & OMAHA_UTX_EMPTY) == 0); ++ UART_PUT_CHAR(port, s[i]); ++ if (s[i] == '\n') { ++ do { ++ status = UART_GET_FR(port); ++ } while ((status & OMAHA_UTX_EMPTY) == 0); ++ UART_PUT_CHAR(port, '\r'); ++ } ++ } ++ ++ /* ++ * Finally, wait for transmitter to become empty ++ * and restore the TCR ++ */ ++ do { ++ status = UART_GET_FR(port); ++ } while ((status & OMAHA_UTX_EMPTY) == 0); ++} ++ ++static kdev_t omahauart_console_device(struct console *co) ++{ ++ return MKDEV(SERIAL_OMAHA_MAJOR, SERIAL_OMAHA_MINOR + co->index); ++} ++ ++static int omahauart_console_wait_key(struct console *co) ++{ ++ struct uart_port *port = omaha_ports + co->index; ++ unsigned int status; ++ ++ do { ++ status = UART_FIFO_STATUS(port); ++ } while (!UART_RX_DATA(status)); ++ return UART_GET_CHAR(port); ++} ++ ++static void __init ++omahauart_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) ++{ ++ // Do nothing. ++} ++ ++static int __init omahauart_console_setup(struct console *co, char *options) ++{ ++ struct uart_port *port; ++ int baud = 38400; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++ ++ /* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++ port = uart_get_console(omaha_ports, UART_NR, co); ++ ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ else ++ omahauart_console_get_options(port, &baud, &parity, &bits); ++ ++ return uart_set_options(port, co, baud, parity, bits, flow); ++} ++ ++static struct console omaha_console = { ++ .write = omahauart_console_write, ++ .device = omahauart_console_device, ++ .wait_key = omahauart_console_wait_key, ++ .setup = omahauart_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++}; ++ ++void __init omahauart_console_init(void) ++{ ++ register_console(&omaha_console); ++} ++ ++#define OMAHA_CONSOLE &omaha_console ++#else ++#define OMAHA_CONSOLE NULL ++#endif ++ ++static struct uart_driver omaha_reg = { ++ .owner = THIS_MODULE, ++ .normal_major = SERIAL_OMAHA_MAJOR, ++#ifdef CONFIG_DEVFS_FS ++ .normal_name = "ttyOM%d", ++ .callout_name = "cuaom%d", ++#else ++ .normal_name = "ttyOM", ++ .callout_name = "cuaom", ++#endif ++ .normal_driver = &normal, ++ .callout_major = CALLOUT_OMAHA_MAJOR, ++ .callout_driver = &callout, ++ .table = omaha_table, ++ .termios = omaha_termios, ++ .termios_locked = omaha_termios_locked, ++ .minor = SERIAL_OMAHA_MINOR, ++ .nr = UART_NR, ++ .port = omaha_ports, ++ .cons = OMAHA_CONSOLE, ++}; ++ ++static int __init omahauart_init(void) ++{ ++ return uart_register_driver(&omaha_reg); ++} ++ ++static void __exit omahauart_exit(void) ++{ ++ uart_unregister_driver(&omaha_reg); ++} ++ ++module_init(omahauart_init); ++module_exit(omahauart_exit); +diff -urN linux-2.4.26/drivers/serial/sa1100.c linux-2.4.26-vrs1/drivers/serial/sa1100.c +--- linux-2.4.26/drivers/serial/sa1100.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/sa1100.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,904 @@ ++/* ++ * linux/drivers/char/serial_sa1100.c ++ * ++ * Driver for SA11x0 serial ports ++ * ++ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. ++ * ++ * Copyright (C) 2000 Deep Blue Solutions Ltd. ++ * ++ * 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 ++ * ++ * $Id: sa1100.c,v 1.14.2.4 2002/10/24 09:53:25 rmk Exp $ ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#if defined(CONFIG_SERIAL_SA1100_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ++#define SUPPORT_SYSRQ ++#endif ++ ++#include ++ ++/* We've been assigned a range on the "Low-density serial ports" major */ ++#define SERIAL_SA1100_MAJOR 204 ++#define CALLOUT_SA1100_MAJOR 205 ++#define MINOR_START 5 ++ ++#define NR_PORTS 3 ++ ++#define SA1100_ISR_PASS_LIMIT 256 ++ ++/* ++ * Convert from ignore_status_mask or read_status_mask to UTSR[01] ++ */ ++#define SM_TO_UTSR0(x) ((x) & 0xff) ++#define SM_TO_UTSR1(x) ((x) >> 8) ++#define UTSR0_TO_SM(x) ((x)) ++#define UTSR1_TO_SM(x) ((x) << 8) ++ ++#define UART_GET_UTCR0(sport) __raw_readl((sport)->port.membase + UTCR0) ++#define UART_GET_UTCR1(sport) __raw_readl((sport)->port.membase + UTCR1) ++#define UART_GET_UTCR2(sport) __raw_readl((sport)->port.membase + UTCR2) ++#define UART_GET_UTCR3(sport) __raw_readl((sport)->port.membase + UTCR3) ++#define UART_GET_UTSR0(sport) __raw_readl((sport)->port.membase + UTSR0) ++#define UART_GET_UTSR1(sport) __raw_readl((sport)->port.membase + UTSR1) ++#define UART_GET_CHAR(sport) __raw_readl((sport)->port.membase + UTDR) ++ ++#define UART_PUT_UTCR0(sport,v) __raw_writel((v),(sport)->port.membase + UTCR0) ++#define UART_PUT_UTCR1(sport,v) __raw_writel((v),(sport)->port.membase + UTCR1) ++#define UART_PUT_UTCR2(sport,v) __raw_writel((v),(sport)->port.membase + UTCR2) ++#define UART_PUT_UTCR3(sport,v) __raw_writel((v),(sport)->port.membase + UTCR3) ++#define UART_PUT_UTSR0(sport,v) __raw_writel((v),(sport)->port.membase + UTSR0) ++#define UART_PUT_UTSR1(sport,v) __raw_writel((v),(sport)->port.membase + UTSR1) ++#define UART_PUT_CHAR(sport,v) __raw_writel((v),(sport)->port.membase + UTDR) ++ ++/* ++ * This is the size of our serial port register set. ++ */ ++#define UART_PORT_SIZE 0x24 ++ ++static struct tty_driver normal, callout; ++static struct tty_struct *sa1100_table[NR_PORTS]; ++static struct termios *sa1100_termios[NR_PORTS], *sa1100_termios_locked[NR_PORTS]; ++static int (*sa1100_open)(struct uart_port *); ++static void (*sa1100_close)(struct uart_port *); ++#ifdef SUPPORT_SYSRQ ++static struct console sa1100_console; ++#endif ++ ++/* ++ * This determines how often we check the modem status signals ++ * for any change. They generally aren't connected to an IRQ ++ * so we have to poll them. We also check immediately before ++ * filling the TX fifo incase CTS has been dropped. ++ */ ++#define MCTRL_TIMEOUT (250*HZ/1000) ++ ++struct sa1100_port { ++ struct uart_port port; ++ struct timer_list timer; ++ unsigned int old_status; ++}; ++ ++/* ++ * Handle any change of modem status signal since we were last called. ++ */ ++static void sa1100_mctrl_check(struct sa1100_port *sport) ++{ ++ unsigned int status, changed; ++ ++ status = sport->port.ops->get_mctrl(&sport->port); ++ changed = status ^ sport->old_status; ++ ++ if (changed == 0) ++ return; ++ ++ sport->old_status = status; ++ ++ if (changed & TIOCM_RI) ++ sport->port.icount.rng++; ++ if (changed & TIOCM_DSR) ++ sport->port.icount.dsr++; ++ if (changed & TIOCM_CAR) ++ uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); ++ if (changed & TIOCM_CTS) ++ uart_handle_cts_change(&sport->port, status & TIOCM_CTS); ++ ++ wake_up_interruptible(&sport->port.info->delta_msr_wait); ++} ++ ++/* ++ * This is our per-port timeout handler, for checking the ++ * modem status signals. ++ */ ++static void sa1100_timeout(unsigned long data) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)data; ++ unsigned long flags; ++ ++ if (sport->port.info) { ++ spin_lock_irqsave(&sport->port.lock, flags); ++ sa1100_mctrl_check(sport); ++ spin_unlock_irqrestore(&sport->port.lock, flags); ++ ++ mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); ++ } ++} ++ ++/* ++ * interrupts disabled on entry ++ */ ++static void sa1100_stop_tx(struct uart_port *port, unsigned int tty_stop) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ u32 utcr3; ++ ++ utcr3 = UART_GET_UTCR3(sport); ++ UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_TIE); ++ sport->port.read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS); ++} ++ ++/* ++ * interrupts may not be disabled on entry ++ */ ++static void sa1100_start_tx(struct uart_port *port, unsigned int tty_start) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ unsigned long flags; ++ u32 utcr3; ++ ++ spin_lock_irqsave(&sport->port.lock, flags); ++ utcr3 = UART_GET_UTCR3(sport); ++ sport->port.read_status_mask |= UTSR0_TO_SM(UTSR0_TFS); ++ UART_PUT_UTCR3(sport, utcr3 | UTCR3_TIE); ++ spin_unlock_irqrestore(&sport->port.lock, flags); ++} ++ ++/* ++ * Interrupts enabled ++ */ ++static void sa1100_stop_rx(struct uart_port *port) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ u32 utcr3; ++ ++ utcr3 = UART_GET_UTCR3(sport); ++ UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_RIE); ++} ++ ++/* ++ * Set the modem control timer to fire immediately. ++ */ ++static void sa1100_enable_ms(struct uart_port *port) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ ++ mod_timer(&sport->timer, jiffies); ++} ++ ++static void ++sa1100_rx_chars(struct sa1100_port *sport, struct pt_regs *regs) ++{ ++ struct tty_struct *tty = sport->port.info->tty; ++ unsigned int status, ch, flg, ignored = 0; ++ ++ status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | ++ UTSR0_TO_SM(UART_GET_UTSR0(sport)); ++ while (status & UTSR1_TO_SM(UTSR1_RNE)) { ++ ch = UART_GET_CHAR(sport); ++ ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) ++ goto ignore_char; ++ sport->port.icount.rx++; ++ ++ flg = TTY_NORMAL; ++ ++ /* ++ * note that the error handling code is ++ * out of the main execution path ++ */ ++ if (status & UTSR1_TO_SM(UTSR1_PRE | UTSR1_FRE | UTSR1_ROR)) ++ goto handle_error; ++ ++ if (uart_handle_sysrq_char(&sport->port, ch, regs)) ++ goto ignore_char; ++ ++ error_return: ++ *tty->flip.flag_buf_ptr++ = flg; ++ *tty->flip.char_buf_ptr++ = ch; ++ tty->flip.count++; ++ ignore_char: ++ status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | ++ UTSR0_TO_SM(UART_GET_UTSR0(sport)); ++ } ++ out: ++ tty_flip_buffer_push(tty); ++ return; ++ ++ handle_error: ++ if (status & UTSR1_TO_SM(UTSR1_PRE)) ++ sport->port.icount.parity++; ++ else if (status & UTSR1_TO_SM(UTSR1_FRE)) ++ sport->port.icount.frame++; ++ if (status & UTSR1_TO_SM(UTSR1_ROR)) ++ sport->port.icount.overrun++; ++ ++ if (status & sport->port.ignore_status_mask) { ++ if (++ignored > 100) ++ goto out; ++ goto ignore_char; ++ } ++ ++ status &= sport->port.read_status_mask; ++ ++ if (status & UTSR1_TO_SM(UTSR1_PRE)) ++ flg = TTY_PARITY; ++ else if (status & UTSR1_TO_SM(UTSR1_FRE)) ++ flg = TTY_FRAME; ++ ++ if (status & UTSR1_TO_SM(UTSR1_ROR)) { ++ /* ++ * overrun does *not* affect the character ++ * we read from the FIFO ++ */ ++ *tty->flip.flag_buf_ptr++ = flg; ++ *tty->flip.char_buf_ptr++ = ch; ++ tty->flip.count++; ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) ++ goto ignore_char; ++ ch = 0; ++ flg = TTY_OVERRUN; ++ } ++#ifdef SUPPORT_SYSRQ ++ sport->port.sysrq = 0; ++#endif ++ goto error_return; ++} ++ ++static void sa1100_tx_chars(struct sa1100_port *sport) ++{ ++ struct circ_buf *xmit = &sport->port.info->xmit; ++ ++ if (sport->port.x_char) { ++ UART_PUT_CHAR(sport, sport->port.x_char); ++ sport->port.icount.tx++; ++ sport->port.x_char = 0; ++ return; ++ } ++ ++ /* ++ * Check the modem control lines before ++ * transmitting anything. ++ */ ++ sa1100_mctrl_check(sport); ++ ++ if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { ++ sa1100_stop_tx(&sport->port, 0); ++ return; ++ } ++ ++ /* ++ * Tried using FIFO (not checking TNF) for fifo fill: ++ * still had the '4 bytes repeated' problem. ++ */ ++ while (UART_GET_UTSR1(sport) & UTSR1_TNF) { ++ UART_PUT_CHAR(sport, xmit->buf[xmit->tail]); ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ sport->port.icount.tx++; ++ if (uart_circ_empty(xmit)) ++ break; ++ } ++ ++ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ++ uart_write_wakeup(&sport->port); ++ ++ if (uart_circ_empty(xmit)) ++ sa1100_stop_tx(&sport->port, 0); ++} ++ ++static void sa1100_int(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct sa1100_port *sport = dev_id; ++ unsigned int status, pass_counter = 0; ++ ++ spin_lock(&sport->port.lock); ++ status = UART_GET_UTSR0(sport); ++ status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS; ++ do { ++ if (status & (UTSR0_RFS | UTSR0_RID)) { ++ /* Clear the receiver idle bit, if set */ ++ if (status & UTSR0_RID) ++ UART_PUT_UTSR0(sport, UTSR0_RID); ++ sa1100_rx_chars(sport, regs); ++ } ++ ++ /* Clear the relevant break bits */ ++ if (status & (UTSR0_RBB | UTSR0_REB)) ++ UART_PUT_UTSR0(sport, status & (UTSR0_RBB | UTSR0_REB)); ++ ++ if (status & UTSR0_RBB) ++ sport->port.icount.brk++; ++ ++ if (status & UTSR0_REB) ++ uart_handle_break(&sport->port); ++ ++ if (status & UTSR0_TFS) ++ sa1100_tx_chars(sport); ++ if (pass_counter++ > SA1100_ISR_PASS_LIMIT) ++ break; ++ status = UART_GET_UTSR0(sport); ++ status &= SM_TO_UTSR0(sport->port.read_status_mask) | ++ ~UTSR0_TFS; ++ } while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID)); ++ spin_unlock(&sport->port.lock); ++} ++ ++/* ++ * Return TIOCSER_TEMT when transmitter is not busy. ++ */ ++static unsigned int sa1100_tx_empty(struct uart_port *port) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ ++ return UART_GET_UTSR1(sport) & UTSR1_TBY ? 0 : TIOCSER_TEMT; ++} ++ ++static unsigned int sa1100_get_mctrl(struct uart_port *port) ++{ ++ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; ++} ++ ++static void sa1100_set_mctrl(struct uart_port *port, unsigned int mctrl) ++{ ++} ++ ++/* ++ * Interrupts always disabled. ++ */ ++static void sa1100_break_ctl(struct uart_port *port, int break_state) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ unsigned long flags; ++ unsigned int utcr3; ++ ++ spin_lock_irqsave(&sport->port.lock, flags); ++ utcr3 = UART_GET_UTCR3(sport); ++ if (break_state == -1) ++ utcr3 |= UTCR3_BRK; ++ else ++ utcr3 &= ~UTCR3_BRK; ++ UART_PUT_UTCR3(sport, utcr3); ++ spin_unlock_irqrestore(&sport->port.lock, flags); ++} ++ ++static int sa1100_startup(struct uart_port *port) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ int retval; ++ ++ /* ++ * Allocate the IRQ ++ */ ++ retval = request_irq(sport->port.irq, sa1100_int, 0, ++ "sa11x0-uart", sport); ++ if (retval) ++ return retval; ++ ++ /* ++ * If there is a specific "open" function ++ * (to register control line interrupts) ++ */ ++ if (sa1100_open) { ++ retval = sa1100_open(port); ++ if (retval) { ++ free_irq(sport->port.irq, sport); ++ return retval; ++ } ++ } ++ ++ /* ++ * Finally, clear and enable interrupts ++ */ ++ UART_PUT_UTSR0(sport, -1); ++ UART_PUT_UTCR3(sport, UTCR3_RXE | UTCR3_TXE | UTCR3_RIE); ++ ++ return 0; ++} ++ ++static void sa1100_shutdown(struct uart_port *port) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ ++ /* ++ * Stop our timer. ++ */ ++ del_timer_sync(&sport->timer); ++ ++ /* ++ * Free the interrupt ++ */ ++ free_irq(sport->port.irq, sport); ++ ++ /* ++ * If there is a specific "close" function (to unregister ++ * control line interrupts) ++ */ ++ if (sa1100_close) ++ sa1100_close(port); ++ ++ /* ++ * Disable all interrupts, port and break condition. ++ */ ++ UART_PUT_UTCR3(sport, 0); ++} ++ ++static void sa1100_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ unsigned long flags; ++ unsigned int utcr0, old_utcr3; ++ ++ if ((cflag & CSIZE) == CS8) ++ utcr0 = UTCR0_DSS; ++ else ++ utcr0 = 0; ++ ++ if (cflag & CSTOPB) ++ utcr0 |= UTCR0_SBS; ++ if (cflag & PARENB) { ++ utcr0 |= UTCR0_PE; ++ if (!(cflag & PARODD)) ++ utcr0 |= UTCR0_OES; ++ } ++ ++ spin_lock_irqsave(&sport->port.lock, flags); ++ ++ sport->port.read_status_mask &= UTSR0_TO_SM(UTSR0_TFS); ++ sport->port.read_status_mask |= UTSR1_TO_SM(UTSR1_ROR); ++ if (iflag & INPCK) ++ sport->port.read_status_mask |= ++ UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE); ++ if (iflag & (BRKINT | PARMRK)) ++ sport->port.read_status_mask |= ++ UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB); ++ ++ /* ++ * Characters to ignore ++ */ ++ sport->port.ignore_status_mask = 0; ++ if (iflag & IGNPAR) ++ sport->port.ignore_status_mask |= ++ UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE); ++ if (iflag & IGNBRK) { ++ sport->port.ignore_status_mask |= ++ UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB); ++ /* ++ * If we're ignoring parity and break indicators, ++ * ignore overruns too (for real raw support). ++ */ ++ if (iflag & IGNPAR) ++ sport->port.ignore_status_mask |= ++ UTSR1_TO_SM(UTSR1_ROR); ++ } ++ ++ del_timer_sync(&sport->timer); ++ ++ /* ++ * disable interrupts and drain transmitter ++ */ ++ old_utcr3 = UART_GET_UTCR3(sport); ++ UART_PUT_UTCR3(sport, old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)); ++ ++ while (UART_GET_UTSR1(sport) & UTSR1_TBY) ++ barrier(); ++ ++ /* then, disable everything */ ++ UART_PUT_UTCR3(sport, 0); ++ ++ /* set the parity, stop bits and data size */ ++ UART_PUT_UTCR0(sport, utcr0); ++ ++ /* set the baud rate */ ++ quot -= 1; ++ UART_PUT_UTCR1(sport, ((quot & 0xf00) >> 8)); ++ UART_PUT_UTCR2(sport, (quot & 0xff)); ++ ++ UART_PUT_UTSR0(sport, -1); ++ ++ UART_PUT_UTCR3(sport, old_utcr3); ++ ++ if (UART_ENABLE_MS(&sport->port, cflag)) ++ sa1100_enable_ms(&sport->port); ++ ++ spin_unlock_irqrestore(&sport->port.lock, flags); ++} ++ ++static const char *sa1100_type(struct uart_port *port) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ ++ return sport->port.type == PORT_SA1100 ? "SA1100" : NULL; ++} ++ ++/* ++ * Release the memory region(s) being used by 'port'. ++ */ ++static void sa1100_release_port(struct uart_port *port) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ ++ release_mem_region(sport->port.mapbase, UART_PORT_SIZE); ++} ++ ++/* ++ * Request the memory region(s) being used by 'port'. ++ */ ++static int sa1100_request_port(struct uart_port *port) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ ++ return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, ++ "sa11x0-uart") != NULL ? 0 : -EBUSY; ++} ++ ++/* ++ * Configure/autoconfigure the port. ++ */ ++static void sa1100_config_port(struct uart_port *port, int flags) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ ++ if (flags & UART_CONFIG_TYPE && ++ sa1100_request_port(&sport->port) == 0) ++ sport->port.type = PORT_SA1100; ++} ++ ++/* ++ * Verify the new serial_struct (for TIOCSSERIAL). ++ * The only change we allow are to the flags and type, and ++ * even then only between PORT_SA1100 and PORT_UNKNOWN ++ */ ++static int sa1100_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ struct sa1100_port *sport = (struct sa1100_port *)port; ++ int ret = 0; ++ ++ if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100) ++ ret = -EINVAL; ++ if (sport->port.irq != ser->irq) ++ ret = -EINVAL; ++ if (ser->io_type != SERIAL_IO_MEM) ++ ret = -EINVAL; ++ if (sport->port.uartclk / 16 != ser->baud_base) ++ ret = -EINVAL; ++ if ((void *)sport->port.mapbase != ser->iomem_base) ++ ret = -EINVAL; ++ if (sport->port.iobase != ser->port) ++ ret = -EINVAL; ++ if (ser->hub6 != 0) ++ ret = -EINVAL; ++ return ret; ++} ++ ++static struct uart_ops sa1100_pops = { ++ .tx_empty = sa1100_tx_empty, ++ .set_mctrl = sa1100_set_mctrl, ++ .get_mctrl = sa1100_get_mctrl, ++ .stop_tx = sa1100_stop_tx, ++ .start_tx = sa1100_start_tx, ++ .stop_rx = sa1100_stop_rx, ++ .enable_ms = sa1100_enable_ms, ++ .break_ctl = sa1100_break_ctl, ++ .startup = sa1100_startup, ++ .shutdown = sa1100_shutdown, ++ .change_speed = sa1100_change_speed, ++ .type = sa1100_type, ++ .release_port = sa1100_release_port, ++ .request_port = sa1100_request_port, ++ .config_port = sa1100_config_port, ++ .verify_port = sa1100_verify_port, ++}; ++ ++static struct sa1100_port sa1100_ports[NR_PORTS]; ++ ++/* ++ * Setup the SA1100 serial ports. Note that we don't include the IrDA ++ * port here since we have our own SIR/FIR driver (see drivers/net/irda) ++ * ++ * Note also that we support "console=ttySAx" where "x" is either 0 or 1. ++ * Which serial port this ends up being depends on the machine you're ++ * running this kernel on. I'm not convinced that this is a good idea, ++ * but that's the way it traditionally works. ++ * ++ * Note that NanoEngine UART3 becomes UART2, and UART2 is no longer ++ * used here. ++ */ ++static void __init sa1100_init_ports(void) ++{ ++ static int first = 1; ++ int i; ++ ++ if (!first) ++ return; ++ first = 0; ++ ++ for (i = 0; i < NR_PORTS; i++) { ++ sa1100_ports[i].port.uartclk = 3686400; ++ sa1100_ports[i].port.ops = &sa1100_pops; ++ sa1100_ports[i].port.fifosize = 8; ++ sa1100_ports[i].port.line = i; ++ sa1100_ports[i].port.iotype = SERIAL_IO_MEM; ++ init_timer(&sa1100_ports[i].timer); ++ sa1100_ports[i].timer.function = sa1100_timeout; ++ sa1100_ports[i].timer.data = (unsigned long)&sa1100_ports[i]; ++ } ++ ++ /* ++ * make transmit lines outputs, so that when the port ++ * is closed, the output is in the MARK state. ++ */ ++ PPDR |= PPC_TXD1 | PPC_TXD3; ++ PPSR |= PPC_TXD1 | PPC_TXD3; ++} ++ ++void __init sa1100_register_uart_fns(struct sa1100_port_fns *fns) ++{ ++ if (fns->enable_ms) ++ sa1100_pops.enable_ms = fns->enable_ms; ++ if (fns->get_mctrl) ++ sa1100_pops.get_mctrl = fns->get_mctrl; ++ if (fns->set_mctrl) ++ sa1100_pops.set_mctrl = fns->set_mctrl; ++ sa1100_open = fns->open; ++ sa1100_close = fns->close; ++ sa1100_pops.pm = fns->pm; ++ sa1100_pops.set_wake = fns->set_wake; ++} ++ ++void __init sa1100_register_uart(int idx, int port) ++{ ++ if (idx >= NR_PORTS) { ++ printk(KERN_ERR "%s: bad index number %d\n", __FUNCTION__, idx); ++ return; ++ } ++ ++ switch (port) { ++ case 1: ++ sa1100_ports[idx].port.membase = (void *)&Ser1UTCR0; ++ sa1100_ports[idx].port.mapbase = _Ser1UTCR0; ++ sa1100_ports[idx].port.irq = IRQ_Ser1UART; ++ sa1100_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; ++ break; ++ ++ case 2: ++ sa1100_ports[idx].port.membase = (void *)&Ser2UTCR0; ++ sa1100_ports[idx].port.mapbase = _Ser2UTCR0; ++ sa1100_ports[idx].port.irq = IRQ_Ser2ICP; ++ sa1100_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; ++ break; ++ ++ case 3: ++ sa1100_ports[idx].port.membase = (void *)&Ser3UTCR0; ++ sa1100_ports[idx].port.mapbase = _Ser3UTCR0; ++ sa1100_ports[idx].port.irq = IRQ_Ser3UART; ++ sa1100_ports[idx].port.flags = ASYNC_BOOT_AUTOCONF; ++ break; ++ ++ default: ++ printk(KERN_ERR "%s: bad port number %d\n", __FUNCTION__, port); ++ } ++} ++ ++ ++#ifdef CONFIG_SERIAL_SA1100_CONSOLE ++ ++/* ++ * Interrupts are disabled on entering ++ */ ++static void ++sa1100_console_write(struct console *co, const char *s, unsigned int count) ++{ ++ struct sa1100_port *sport = &sa1100_ports[co->index]; ++ unsigned int old_utcr3, status, i; ++ ++ /* ++ * First, save UTCR3 and then disable interrupts ++ */ ++ old_utcr3 = UART_GET_UTCR3(sport); ++ UART_PUT_UTCR3(sport, (old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)) | ++ UTCR3_TXE); ++ ++ /* ++ * Now, do each character ++ */ ++ for (i = 0; i < count; i++) { ++ do { ++ status = UART_GET_UTSR1(sport); ++ } while (!(status & UTSR1_TNF)); ++ UART_PUT_CHAR(sport, s[i]); ++ if (s[i] == '\n') { ++ do { ++ status = UART_GET_UTSR1(sport); ++ } while (!(status & UTSR1_TNF)); ++ UART_PUT_CHAR(sport, '\r'); ++ } ++ } ++ ++ /* ++ * Finally, wait for transmitter to become empty ++ * and restore UTCR3 ++ */ ++ do { ++ status = UART_GET_UTSR1(sport); ++ } while (status & UTSR1_TBY); ++ UART_PUT_UTCR3(sport, old_utcr3); ++} ++ ++static kdev_t sa1100_console_device(struct console *co) ++{ ++ return MKDEV(SERIAL_SA1100_MAJOR, MINOR_START + co->index); ++} ++ ++/* ++ * If the port was already initialised (eg, by a boot loader), try to determine ++ * the current setup. ++ */ ++static void __init ++sa1100_console_get_options(struct sa1100_port *sport, int *baud, ++ int *parity, int *bits) ++{ ++ unsigned int utcr3; ++ ++ utcr3 = UART_GET_UTCR3(sport) & (UTCR3_RXE | UTCR3_TXE); ++ if (utcr3 == (UTCR3_RXE | UTCR3_TXE)) { ++ /* ok, the port was enabled */ ++ unsigned int utcr0, quot; ++ ++ utcr0 = UART_GET_UTCR0(sport); ++ ++ *parity = 'n'; ++ if (utcr0 & UTCR0_PE) { ++ if (utcr0 & UTCR0_OES) ++ *parity = 'e'; ++ else ++ *parity = 'o'; ++ } ++ ++ if (utcr0 & UTCR0_DSS) ++ *bits = 8; ++ else ++ *bits = 7; ++ ++ quot = UART_GET_UTCR2(sport) | UART_GET_UTCR1(sport) << 8; ++ quot &= 0xfff; ++ *baud = sport->port.uartclk / (16 * (quot + 1)); ++ } ++} ++ ++static int __init ++sa1100_console_setup(struct console *co, char *options) ++{ ++ struct sa1100_port *sport; ++ int baud = CONFIG_SA1100_DEFAULT_BAUDRATE; ++ int bits = 8; ++ int parity = 'n'; ++ int flow = 'n'; ++ ++ /* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++ if (co->index == -1 || co->index >= NR_PORTS) ++ co->index = 0; ++ sport = &sa1100_ports[co->index]; ++ ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ else ++ sa1100_console_get_options(sport, &baud, &parity, &bits); ++ ++ return uart_set_options(&sport->port, co, baud, parity, bits, flow); ++} ++ ++static struct console sa1100_console = { ++ .name = "ttySA", ++ .write = sa1100_console_write, ++ .device = sa1100_console_device, ++ .setup = sa1100_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = -1, ++}; ++ ++void __init sa1100_rs_console_init(void) ++{ ++ sa1100_init_ports(); ++ register_console(&sa1100_console); ++} ++ ++#define SA1100_CONSOLE &sa1100_console ++#else ++#define SA1100_CONSOLE NULL ++#endif ++ ++static struct uart_driver sa1100_reg = { ++ .owner = THIS_MODULE, ++ .normal_major = SERIAL_SA1100_MAJOR, ++#ifdef CONFIG_DEVFS_FS ++ .normal_name = "ttySA%d", ++ .callout_name = "cusa%d", ++#else ++ .normal_name = "ttySA", ++ .callout_name = "cusa", ++#endif ++ .normal_driver = &normal, ++ .callout_major = CALLOUT_SA1100_MAJOR, ++ .callout_driver = &callout, ++ .table = sa1100_table, ++ .termios = sa1100_termios, ++ .termios_locked = sa1100_termios_locked, ++ .minor = MINOR_START, ++ .nr = NR_PORTS, ++ .cons = SA1100_CONSOLE, ++}; ++ ++static int __init sa1100_serial_init(void) ++{ ++ int i, ret; ++ ++ sa1100_init_ports(); ++ ++ ret = uart_register_driver(&sa1100_reg); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < NR_PORTS; i++) ++ uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port); ++ ++ return 0; ++} ++ ++static void __exit sa1100_serial_exit(void) ++{ ++ int i; ++ ++ for (i = 0; i < NR_PORTS; i++) ++ uart_remove_one_port(&sa1100_reg, &sa1100_ports[i].port); ++ ++ uart_unregister_driver(&sa1100_reg); ++} ++ ++module_init(sa1100_serial_init); ++module_exit(sa1100_serial_exit); ++ ++EXPORT_NO_SYMBOLS; ++ ++MODULE_AUTHOR("Deep Blue Solutions Ltd"); ++MODULE_DESCRIPTION("SA1100 generic serial port driver"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/serial/uart00.c linux-2.4.26-vrs1/drivers/serial/uart00.c +--- linux-2.4.26/drivers/serial/uart00.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/serial/uart00.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,903 @@ ++/* ++ * linux/drivers/serial/uart00.c ++ * ++ * Driver for UART00 serial ports ++ * ++ * Based on drivers/char/serial_amba.c, by ARM Limited & ++ * Deep Blue Solutions Ltd. ++ * Copyright 2001 Altera Corporation ++ * ++ * 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 ++ * ++ * $Id: uart00.c,v 1.3.2.5 2002/10/24 09:53:26 rmk Exp $ ++ * ++ */ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if defined(CONFIG_SERIAL_UART00_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ++#define SUPPORT_SYSRQ ++#endif ++ ++#include ++#include ++#define UART00_TYPE (volatile unsigned int*) ++#include ++#include ++ ++#undef DEBUG ++#define UART_NR 2 ++ ++#define SERIAL_UART00_NAME "ttyUA" ++#define SERIAL_UART00_MAJOR 204 ++#define SERIAL_UART00_MINOR 16 /* Temporary - will change in future */ ++#define SERIAL_UART00_NR UART_NR ++#define UART_PORT_SIZE 0x50 ++ ++#define CALLOUT_UART00_NAME "cuaua" ++#define CALLOUT_UART00_MAJOR 205 ++#define CALLOUT_UART00_MINOR 16 /* Temporary - will change in future */ ++#define CALLOUT_UART00_NR UART_NR ++ ++static struct tty_driver normal, callout; ++static struct tty_struct *uart00_table[UART_NR]; ++static struct termios *uart00_termios[UART_NR], *uart00_termios_locked[UART_NR]; ++static struct console uart00_console; ++static struct uart_driver uart00_reg; ++ ++ ++#define UART00_ISR_PASS_LIMIT 256 ++ ++/* ++ * Access macros for the UART00 UARTs ++ */ ++#define UART_GET_INT_STATUS(p) inl(UART_ISR((p)->membase)) ++#define UART_PUT_IES(p, c) outl(c,UART_IES((p)->membase)) ++#define UART_GET_IES(p) inl(UART_IES((p)->membase)) ++#define UART_PUT_IEC(p, c) outl(c,UART_IEC((p)->membase)) ++#define UART_GET_IEC(p) inl(UART_IEC((p)->membase)) ++#define UART_PUT_CHAR(p, c) outl(c,UART_TD((p)->membase)) ++#define UART_GET_CHAR(p) inl(UART_RD((p)->membase)) ++#define UART_GET_RSR(p) inl(UART_RSR((p)->membase)) ++#define UART_GET_RDS(p) inl(UART_RDS((p)->membase)) ++#define UART_GET_MSR(p) inl(UART_MSR((p)->membase)) ++#define UART_GET_MCR(p) inl(UART_MCR((p)->membase)) ++#define UART_PUT_MCR(p, c) outl(c,UART_MCR((p)->membase)) ++#define UART_GET_MC(p) inl(UART_MC((p)->membase)) ++#define UART_PUT_MC(p, c) outl(c,UART_MC((p)->membase)) ++#define UART_GET_TSR(p) inl(UART_TSR((p)->membase)) ++#define UART_GET_DIV_HI(p) inl(UART_DIV_HI((p)->membase)) ++#define UART_PUT_DIV_HI(p,c) outl(c,UART_DIV_HI((p)->membase)) ++#define UART_GET_DIV_LO(p) inl(UART_DIV_LO((p)->membase)) ++#define UART_PUT_DIV_LO(p,c) outl(c,UART_DIV_LO((p)->membase)) ++#define UART_RX_DATA(s) ((s) & UART_RSR_RX_LEVEL_MSK) ++#define UART_TX_READY(s) (((s) & UART_TSR_TX_LEVEL_MSK) < 15) ++//#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & UART00_UARTFR_TMSK) == 0) ++ ++static void uart00_stop_tx(struct uart_port *port, u_int from_tty) ++{ ++ ++ UART_PUT_IEC(port, UART_IEC_TIE_MSK); ++} ++ ++static void uart00_stop_rx(struct uart_port *port) ++{ ++ ++ UART_PUT_IEC(port, UART_IEC_RE_MSK); ++} ++ ++static void uart00_enable_ms(struct uart_port *port) ++{ ++ ++ UART_PUT_IES(port, UART_IES_ME_MSK); ++} ++ ++static void ++uart00_rx_chars(struct uart_port *port, struct pt_regs *regs) ++{ ++ struct uart_info *info = port->info; ++ struct tty_struct *tty = info->tty; ++ unsigned int status, ch, rds, flg, ignored = 0; ++ ++ ++ status = UART_GET_RSR(port); ++ while (UART_RX_DATA(status)) { ++ ++ /* ++ * We need to read rds before reading the ++ * character from the fifo ++ */ ++ rds = UART_GET_RDS(port); ++ ch = UART_GET_CHAR(port); ++ port->icount.rx++; ++ ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) ++ goto ignore_char; ++ ++ flg = TTY_NORMAL; ++ ++ /* ++ * Note that the error handling code is ++ * out of the main execution path ++ */ ++ if (rds & (UART_RDS_BI_MSK |UART_RDS_FE_MSK|UART_RDS_PE_MSK)) ++ goto handle_error; ++ if (uart_handle_sysrq_char(port, ch, regs)) ++ goto ignore_char; ++ ++ error_return: ++ *tty->flip.flag_buf_ptr++ = flg; ++ *tty->flip.char_buf_ptr++ = ch; ++ tty->flip.count++; ++ ignore_char: ++ status = UART_GET_RSR(port); ++ } ++out: ++ tty_flip_buffer_push(tty); ++ return; ++ ++handle_error: ++ if (rds & UART_RDS_BI_MSK) { ++ status &= ~(UART_RDS_FE_MSK | UART_RDS_PE_MSK); ++ port->icount.brk++; ++ ++#ifdef SUPPORT_SYSRQ ++ if (uart_handle_break(port)) ++ goto ignore_char; ++#endif ++ } else if (rds & UART_RDS_PE_MSK) ++ port->icount.parity++; ++ else if (rds & UART_RDS_FE_MSK) ++ port->icount.frame++; ++ if (rds & UART_RDS_OE_MSK) ++ port->icount.overrun++; ++ ++ if (rds & port->ignore_status_mask) { ++ if (++ignored > 100) ++ goto out; ++ goto ignore_char; ++ } ++ rds &= port->read_status_mask; ++ ++ if (rds & UART_RDS_BI_MSK) ++ flg = TTY_BREAK; ++ else if (rds & UART_RDS_PE_MSK) ++ flg = TTY_PARITY; ++ else if (rds & UART_RDS_FE_MSK) ++ flg = TTY_FRAME; ++ ++ if (rds & UART_RDS_OE_MSK) { ++ /* ++ * CHECK: does overrun affect the current character? ++ * ASSUMPTION: it does not. ++ */ ++ *tty->flip.flag_buf_ptr++ = flg; ++ *tty->flip.char_buf_ptr++ = ch; ++ tty->flip.count++; ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) ++ goto ignore_char; ++ ch = 0; ++ flg = TTY_OVERRUN; ++ } ++#ifdef SUPPORT_SYSRQ ++ port->sysrq = 0; ++#endif ++ goto error_return; ++} ++ ++static void uart00_tx_chars(struct uart_port *port) ++{ ++ int count; ++ struct uart_info *info = port->info; ++ ++ if (port->x_char) { ++ while((UART_GET_TSR(port)& UART_TSR_TX_LEVEL_MSK)==15); ++ UART_PUT_CHAR(port, port->x_char); ++ port->icount.tx++; ++ port->x_char = 0; ++ ++ return; ++ } ++ if (info->xmit.head == info->xmit.tail ++ || info->tty->stopped ++ || info->tty->hw_stopped) { ++ uart00_stop_tx(port, 0); ++ return; ++ } ++ ++ count = port->fifosize >> 1; ++ do { ++ while((UART_GET_TSR(port)& UART_TSR_TX_LEVEL_MSK)==15); ++ UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]); ++ info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1); ++ port->icount.tx++; ++ if (info->xmit.head == info->xmit.tail) ++ break; ++ } while (--count > 0); ++ ++ if (CIRC_CNT(info->xmit.head, ++ info->xmit.tail, ++ UART_XMIT_SIZE) < WAKEUP_CHARS) ++ uart_write_wakeup(port); ++ ++ if (info->xmit.head == info->xmit.tail) ++ uart00_stop_tx(port, 0); ++} ++ ++static void uart00_start_tx(struct uart_port *port, u_int from_tty) ++{ ++ UART_PUT_IES(port,UART_IES_TIE_MSK ); ++ uart00_tx_chars(port); ++} ++ ++static void uart00_modem_status(struct uart_port *port) ++{ ++ unsigned int status; ++ struct uart_icount *icount = &port->icount; ++ struct uart_info *info = port->info; ++ ++ status = UART_GET_MSR(port); ++ ++ if (!status & (UART_MSR_DCTS_MSK | UART_MSR_DDSR_MSK | ++ UART_MSR_TERI_MSK | UART_MSR_DDCD_MSK)) ++ return; ++ ++ if (status & UART_MSR_DDCD_MSK) { ++ icount->dcd++; ++#ifdef CONFIG_HARD_PPS ++ if ((port->flags & ASYNC_HARDPPS_CD) && ++ (status & UART_MSR_DCD_MSK)) ++ hardpps(); ++#endif ++ if (info->flags & ASYNC_CHECK_CD) { ++ if (status & UART_MSR_DCD_MSK) ++ wake_up_interruptible(&info->open_wait); ++ else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && ++ (port->flags & ASYNC_CALLOUT_NOHUP))) { ++ if (info->tty) ++ tty_hangup(info->tty); ++ } ++ } ++ } ++ ++ if (status & UART_MSR_DDSR_MSK) ++ icount->dsr++; ++ ++ if (status & UART_MSR_DCTS_MSK) { ++ icount->cts++; ++ ++ if (info->flags & ASYNC_CTS_FLOW) { ++ status &= UART_MSR_CTS_MSK; ++ ++ if (info->tty->hw_stopped) { ++ if (status) { ++ info->tty->hw_stopped = 0; ++ port->ops->start_tx(port, 0); ++ uart_write_wakeup(port); ++ } ++ } else { ++ if (!status) { ++ info->tty->hw_stopped = 1; ++ port->ops->stop_tx(port, 0); ++ } ++ } ++ } ++ } ++ wake_up_interruptible(&info->delta_msr_wait); ++ ++} ++ ++static void uart00_int(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct uart_port *port = dev_id; ++ unsigned int status, pass_counter = 0; ++ ++ status = UART_GET_INT_STATUS(port); ++ do { ++ ++ if (status & UART_ISR_RI_MSK) ++ uart00_rx_chars(port, regs); ++ if (status & (UART_ISR_TI_MSK | UART_ISR_TII_MSK)) ++ uart00_tx_chars(port); ++ if (status & UART_ISR_MI_MSK) ++ uart00_modem_status(port); ++ if (pass_counter++ > UART00_ISR_PASS_LIMIT) ++ break; ++ ++ status = UART_GET_INT_STATUS(port); ++ } while (status); ++} ++ ++static u_int uart00_tx_empty(struct uart_port *port) ++{ ++ return UART_GET_TSR(port) & UART_TSR_TX_LEVEL_MSK? 0 : TIOCSER_TEMT; ++} ++ ++static u_int uart00_get_mctrl(struct uart_port *port) ++{ ++ unsigned int result = 0; ++ unsigned int status; ++ ++ status = UART_GET_MSR(port); ++ if (status & UART_MSR_DCD_MSK) ++ result |= TIOCM_CAR; ++ if (status & UART_MSR_DSR_MSK) ++ result |= TIOCM_DSR; ++ if (status & UART_MSR_CTS_MSK) ++ result |= TIOCM_CTS; ++ if (status & UART_MCR_RI_MSK) ++ result |= TIOCM_RNG; ++ ++ return result; ++} ++ ++static void uart00_set_mctrl(struct uart_port *port, u_int mctrl) ++{ ++ unsigned char mcr = 0; ++ ++ if (mctrl & TIOCM_RTS) ++ mcr |= UART_MCR_RTS_MSK; ++ if (mctrl & TIOCM_DTR) ++ mcr |= UART_MCR_DTR_MSK; ++ if (mctrl & TIOCM_LOOP) ++ mcr |= UART_MCR_LB_MSK; ++ ++ UART_PUT_MCR(port, mcr); ++} ++ ++static void uart00_break_ctl(struct uart_port *port, int break_state) ++{ ++ unsigned int mcr; ++ ++ mcr = UART_GET_MCR(port); ++ if (break_state == -1) ++ mcr |= UART_MCR_BR_MSK; ++ else ++ mcr &= ~UART_MCR_BR_MSK; ++ UART_PUT_MCR(port, mcr); ++} ++ ++static inline u_int uart_calculate_quot(struct uart_port *port, u_int baud) ++{ ++ u_int quot; ++ ++ /* Special case: B0 rate */ ++ if (!baud) ++ baud = 9600; ++ ++ quot = (port->uartclk / (16 * baud)-1) ; ++ ++ return quot; ++} ++static void uart00_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot) ++{ ++ u_int uart_mc=0, old_ies; ++ unsigned long flags; ++ ++#ifdef DEBUG ++ printk("uart00_set_cflag(0x%x) called\n", cflag); ++#endif ++ /* byte size and parity */ ++ switch (cflag & CSIZE) { ++ case CS5: uart_mc = UART_MC_CLS_CHARLEN_5; break; ++ case CS6: uart_mc = UART_MC_CLS_CHARLEN_6; break; ++ case CS7: uart_mc = UART_MC_CLS_CHARLEN_7; break; ++ default: uart_mc = UART_MC_CLS_CHARLEN_8; break; // CS8 ++ } ++ if (cflag & CSTOPB) ++ uart_mc|= UART_MC_ST_TWO; ++ if (cflag & PARENB) { ++ uart_mc |= UART_MC_PE_MSK; ++ if (!(cflag & PARODD)) ++ uart_mc |= UART_MC_EP_MSK; ++ } ++ ++ port->read_status_mask = UART_RDS_OE_MSK; ++ if (iflag & INPCK) ++ port->read_status_mask |= UART_RDS_FE_MSK | UART_RDS_PE_MSK; ++ if (iflag & (BRKINT | PARMRK)) ++ port->read_status_mask |= UART_RDS_BI_MSK; ++ ++ /* ++ * Characters to ignore ++ */ ++ port->ignore_status_mask = 0; ++ if (iflag & IGNPAR) ++ port->ignore_status_mask |= UART_RDS_FE_MSK | UART_RDS_PE_MSK; ++ if (iflag & IGNBRK) { ++ port->ignore_status_mask |= UART_RDS_BI_MSK; ++ /* ++ * If we're ignoring parity and break indicators, ++ * ignore overruns to (for real raw support). ++ */ ++ if (iflag & IGNPAR) ++ port->ignore_status_mask |= UART_RDS_OE_MSK; ++ } ++ ++ /* first, disable everything */ ++ save_flags(flags); cli(); ++ old_ies = UART_GET_IES(port); ++ ++ if ((port->flags & ASYNC_HARDPPS_CD) || ++ (cflag & CRTSCTS) || !(cflag & CLOCAL)) ++ old_ies |= UART_IES_ME_MSK; ++ ++ ++ /* Set baud rate */ ++ UART_PUT_DIV_LO(port, (quot & 0xff)); ++ UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8)); ++ ++ ++ UART_PUT_MC(port, uart_mc); ++ UART_PUT_IES(port, old_ies); ++ ++ restore_flags(flags); ++} ++ ++static int uart00_startup(struct uart_port *port) ++{ ++ int retval; ++ ++ /* ++ * Allocate the IRQ ++ */ ++ retval = request_irq(port->irq, uart00_int, 0, "uart00", port); ++ if (retval) ++ return retval; ++ ++ /* ++ * Finally, enable interrupts. Use the TII interrupt to minimise ++ * the number of interrupts generated. If higher performance is ++ * needed, consider using the TI interrupt with a suitable FIFO ++ * threshold ++ */ ++ UART_PUT_IES(port, UART_IES_RE_MSK | UART_IES_TIE_MSK); ++ ++ return 0; ++} ++ ++static void uart00_shutdown(struct uart_port *port) ++{ ++ /* ++ * disable all interrupts, disable the port ++ */ ++ UART_PUT_IEC(port, 0xff); ++ ++ /* disable break condition and fifos */ ++ UART_PUT_MCR(port, UART_GET_MCR(port) &~UART_MCR_BR_MSK); ++ ++ /* ++ * Free the interrupt ++ */ ++ free_irq(port->irq, port); ++} ++ ++static const char *uart00_type(struct uart_port *port) ++{ ++ return port->type == PORT_UART00 ? "Altera UART00" : NULL; ++} ++ ++/* ++ * Release the memory region(s) being used by 'port' ++ */ ++static void uart00_release_port(struct uart_port *port) ++{ ++ release_mem_region(port->mapbase, UART_PORT_SIZE); ++ ++#ifdef CONFIG_ARCH_CAMELOT ++ if(port->membase!=(void*)IO_ADDRESS(EXC_UART00_BASE)){ ++ iounmap(port->membase); ++ } ++#endif ++} ++ ++/* ++ * Request the memory region(s) being used by 'port' ++ */ ++static int uart00_request_port(struct uart_port *port) ++{ ++ int result; ++ ++ result = request_mem_region(port->mapbase, UART_PORT_SIZE, ++ "serial_uart00") != NULL ? 0 : -EBUSY; ++ if (result) ++ return result; ++ ++ port->membase = ioremap(port->mapbase, SZ_4K); ++ if (!port->membase) { ++ printk(KERN_ERR "serial00: cannot map io memory\n"); ++ release_mem_region(port->mapbase, UART_PORT_SIZE); ++ } ++ ++ return port->membase ? 0 : -ENOMEM; ++} ++ ++/* ++ * Configure/autoconfigure the port. ++ */ ++static void uart00_config_port(struct uart_port *port, int flags) ++{ ++ if (flags & UART_CONFIG_TYPE) { ++ if (uart00_request_port(port) == 0) ++ port->type = PORT_UART00; ++ } ++} ++ ++/* ++ * verify the new serial_struct (for TIOCSSERIAL). ++ */ ++static int uart00_verify_port(struct uart_port *port, struct serial_struct *ser) ++{ ++ int ret = 0; ++ if (ser->type != PORT_UNKNOWN && ser->type != PORT_UART00) ++ ret = -EINVAL; ++ if (ser->irq < 0 || ser->irq >= NR_IRQS) ++ ret = -EINVAL; ++ if (ser->baud_base < 9600) ++ ret = -EINVAL; ++ return ret; ++} ++ ++static struct uart_ops uart00_pops = { ++ tx_empty: uart00_tx_empty, ++ set_mctrl: uart00_set_mctrl, ++ get_mctrl: uart00_get_mctrl, ++ stop_tx: uart00_stop_tx, ++ start_tx: uart00_start_tx, ++ stop_rx: uart00_stop_rx, ++ enable_ms: uart00_enable_ms, ++ break_ctl: uart00_break_ctl, ++ startup: uart00_startup, ++ shutdown: uart00_shutdown, ++ change_speed: uart00_change_speed, ++ type: uart00_type, ++ release_port: uart00_release_port, ++ request_port: uart00_request_port, ++ config_port: uart00_config_port, ++ verify_port: uart00_verify_port, ++}; ++ ++static struct uart_port uart00_ports[UART_NR] = { ++ ++#ifdef CONFIG_ARCH_CAMELOT ++{ ++ .membase = (void*)IO_ADDRESS(EXC_UART00_BASE), ++ .mapbase = EXC_UART00_BASE, ++ .iotype = SERIAL_IO_MEM, ++ .irq = IRQ_UART, ++ .uartclk = EXC_AHB2_CLK_FREQUENCY, ++ .fifosize = 16, ++ .ops = &uart00_pops, ++ .flags = ASYNC_BOOT_AUTOCONF, ++} ++#endif ++}; ++ ++#ifdef CONFIG_SERIAL_UART00_CONSOLE ++static void uart00_console_write(struct console *co, const char *s, unsigned count) ++{ ++#ifdef CONFIG_ARCH_CAMELOT ++ struct uart_port *port = &uart00_ports[0]; ++ unsigned int status, old_ies; ++ int i; ++ ++ /* ++ * First save the CR then disable the interrupts ++ */ ++ old_ies = UART_GET_IES(port); ++ UART_PUT_IEC(port,0xff); ++ ++ /* ++ * Now, do each character ++ */ ++ for (i = 0; i < count; i++) { ++ do { ++ status = UART_GET_TSR(port); ++ } while (!UART_TX_READY(status)); ++ UART_PUT_CHAR(port, s[i]); ++ if (s[i] == '\n') { ++ do { ++ status = UART_GET_TSR(port); ++ } while (!UART_TX_READY(status)); ++ UART_PUT_CHAR(port, '\r'); ++ } ++ } ++ ++ /* ++ * Finally, wait for transmitter to become empty ++ * and restore the IES ++ */ ++ do { ++ status = UART_GET_TSR(port); ++ } while (status & UART_TSR_TX_LEVEL_MSK); ++ UART_PUT_IES(port, old_ies); ++#endif ++} ++ ++static kdev_t uart00_console_device(struct console *co) ++{ ++ return MKDEV(SERIAL_UART00_MAJOR, SERIAL_UART00_MINOR + co->index); ++} ++ ++static void /*__init*/ uart00_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) ++{ ++ u_int uart_mc, quot; ++ uart_mc= UART_GET_MC(port); ++ ++ *parity = 'n'; ++ if (uart_mc & UART_MC_PE_MSK) { ++ if (uart_mc & UART_MC_EP_MSK) ++ *parity = 'e'; ++ else ++ *parity = 'o'; ++ } ++ ++ switch (uart_mc & UART_MC_CLS_MSK){ ++ ++ case UART_MC_CLS_CHARLEN_5: ++ *bits = 5; ++ break; ++ case UART_MC_CLS_CHARLEN_6: ++ *bits = 6; ++ break; ++ case UART_MC_CLS_CHARLEN_7: ++ *bits = 7; ++ break; ++ case UART_MC_CLS_CHARLEN_8: ++ *bits = 8; ++ break; ++ } ++ quot = UART_GET_DIV_LO(port) | (UART_GET_DIV_HI(port) << 8); ++ *baud = port->uartclk / (16 *quot ); ++} ++ ++static int __init uart00_console_setup(struct console *co, char *options) ++{ ++ struct uart_port *port; ++ int baud = 38400; ++ int bits = 8; ++ int parity = 'n'; ++ int flow= 'n'; ++ ++#ifdef CONFIG_ARCH_CAMELOT ++ /* ++ * Check whether an invalid uart number has been specified, and ++ * if so, search for the first available port that does have ++ * console support. ++ */ ++ port = &uart00_ports[0]; ++ co->index = 0; ++#else ++ return -ENODEV; ++#endif ++ ++ if (options) ++ uart_parse_options(options, &baud, &parity, &bits, &flow); ++ else ++ uart00_console_get_options(port, &baud, &parity, &bits); ++ ++ return uart_set_options(port, co, baud, parity, bits, flow); ++} ++ ++static struct console uart00_console = { ++ .name = SERIAL_UART00_NAME, ++ .write = uart00_console_write, ++ .device = uart00_console_device, ++ .setup = uart00_console_setup, ++ .flags = CON_PRINTBUFFER, ++ .index = 0, ++}; ++ ++void __init uart00_console_init(void) ++{ ++ register_console(&uart00_console); ++} ++ ++#define UART00_CONSOLE &uart00_console ++#else ++#define UART00_CONSOLE NULL ++#endif ++ ++static struct uart_driver uart00_reg = { ++ .owner = NULL, ++ .normal_major = SERIAL_UART00_MAJOR, ++ .normal_name = SERIAL_UART00_NAME, ++ .normal_driver = &normal, ++ .callout_major = CALLOUT_UART00_MAJOR, ++ .callout_name = CALLOUT_UART00_NAME, ++ .callout_driver = &callout, ++ .table = uart00_table, ++ .termios = uart00_termios, ++ .termios_locked = uart00_termios_locked, ++ .minor = SERIAL_UART00_MINOR, ++ .nr = UART_NR, ++ .state = NULL, ++ .cons = UART00_CONSOLE, ++}; ++ ++struct dev_port_entry{ ++ struct uart_port *port; ++}; ++ ++static struct dev_port_entry dev_port_map[UART_NR]; ++ ++#ifdef CONFIG_PLD_HOTSWAP ++/* ++ * Keep a mapping of dev_info addresses -> port lines to use when ++ * removing ports dev==NULL indicates unused entry ++ */ ++ ++struct uart00_ps_data{ ++ unsigned int clk; ++ unsigned int fifosize; ++}; ++ ++int uart00_add_device(struct pldhs_dev_info* dev_info, void* dev_ps_data) ++{ ++ struct uart00_ps_data* dev_ps=dev_ps_data; ++ struct uart_port * port; ++ int i,result; ++ ++ i=0; ++ while(dev_port_map[i].port) ++ i++; ++ ++ if(i==UART_NR){ ++ printk(KERN_WARNING "uart00: Maximum number of ports reached\n"); ++ return 0; ++ } ++ ++ port=&uart00_ports[i]; ++ ++ printk("clk=%d fifo=%d\n",dev_ps->clk,dev_ps->fifosize); ++ port->membase=0; ++ port->mapbase=dev_info->base_addr; ++ port->iotype=SERIAL_IO_MEM; ++ port->irq=dev_info->irq; ++ port->uartclk=dev_ps->clk; ++ port->fifosize=dev_ps->fifosize; ++ port->ops=&uart00_pops; ++ port->line=i; ++ port->flags=ASYNC_BOOT_AUTOCONF; ++ ++ result=uart_register_port(&uart00_reg, port); ++ if(result<0){ ++ printk("uart_register_port returned %d\n",result); ++ return result; ++ } ++ dev_port_map[i].port=port; ++ printk("uart00: added device at %lx as ttyUA%d\n",dev_port_map[i].port->mapbase,i); ++ return 0; ++ ++} ++ ++int uart00_remove_devices(void) ++{ ++ int i,result; ++ ++ ++ result=0; ++ for(i=1;iuartclk,port->fifosize); ++ len+=PLDHS_READ_PROC_DATA(buf+len,"uart00",i, ++ port->mapbase,port->irq,ps_data); ++ ++ } ++ } ++ *eof=1; ++ return len; ++} ++ ++ ++ ++#endif ++struct pld_hotswap_ops uart00_pldhs_ops={ ++ .name = "uart00", ++ .add_device = uart00_add_device, ++ .remove_devices = uart00_remove_devices, ++ .proc_read = uart00_proc_read ++}; ++ ++#endif ++ ++static int __init uart00_init(void) ++{ ++ int ret; ++ int i; ++ ++ ret = uart_register_driver(&uart00_reg); ++ if (ret) { ++ printk(KERN_ERR "uart00: Couldn't register driver\n"); ++ return ret; ++ } ++ ++ unregister_console(&uart00_console); ++ ++ for(i=0;i ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License. ++ * ++ * This is the machine specific part of the Assabet/UDA1341 support. ++ * This driver makes use of the UDA1341 and the sa1100-audio modules. ++ * ++ * History: ++ * ++ * 2000-05-21 Nicolas Pitre Initial release. ++ * ++ * 2001-06-03 Nicolas Pitre Made this file a separate module, based on ++ * the former sa1100-uda1341.c driver. ++ * ++ * 2001-07-17 Nicolas Pitre Supports 44100Hz and 22050Hz samplerate now. ++ * ++ * 2001-08-03 Russell King Fix left/right channel swap. ++ * Attempt to reduce power consumption when idle. ++ * ++ * 2001-09-23 Russell King Remove old L3 bus driver. ++ * ++ * Please note that fiddling too much with MDREFR results in oopses, so we don't ++ * touch MDREFR unnecessarily, which means we don't touch it on close. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "sa1100-audio.h" ++ ++/* ++ * Define this to fix the power drain on early Assabets ++ */ ++#define FIX_POWER_DRAIN ++ ++/* ++ * Debugging? ++ */ ++#undef DEBUG ++ ++ ++#ifdef DEBUG ++#define DPRINTK( x... ) printk( ##x ) ++#else ++#define DPRINTK( x... ) ++#endif ++ ++ ++#define AUDIO_RATE_DEFAULT 44100 ++ ++/* ++ * Mixer (UDA1341) interface ++ */ ++ ++static struct l3_client uda1341; ++ ++static int ++mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) ++{ ++ /* ++ * We only accept mixer (type 'M') ioctls. ++ */ ++ if (_IOC_TYPE(cmd) != 'M') ++ return -EINVAL; ++ ++ return l3_command(&uda1341, cmd, (void *)arg); ++} ++ ++static struct file_operations assabet_mixer_fops = { ++ ioctl: mixer_ioctl, ++ owner: THIS_MODULE ++}; ++ ++ ++/* ++ * Audio interface ++ */ ++static long audio_samplerate = AUDIO_RATE_DEFAULT; ++ ++/* ++ * FIXME: what about SFRM going high when SSP is disabled? ++ */ ++static void assabet_set_samplerate(long val) ++{ ++ struct uda1341_cfg cfg; ++ u_int clk_ref, clk_div; ++ ++ /* We don't want to mess with clocks when frames are in flight */ ++ Ser4SSCR0 &= ~SSCR0_SSE; ++ /* wait for any frame to complete */ ++ udelay(125); ++ ++ /* ++ * Our clock source is derived from the CPLD on which we don't have ++ * much control unfortunately. This was intended for a fixed 48000 Hz ++ * samplerate assuming a core clock of 221.2 MHz. The CPLD appears ++ * to divide the memory clock so there is a ratio of 4608 between ++ * the core clock and the resulting samplerate (obtained by ++ * measurements, the CPLD equations should confirm that). ++ * ++ * Still we can play with the SA1110's clock divisor for the SSP port ++ * to get half the samplerate as well. ++ * ++ * Apparently the clock sent to the SA1110 for the SSP port is further ++ * more divided from the clock sent to the UDA1341 (some people tried ++ * to be too clever in their design, or simply failed to read the ++ * SA1110 manual). If it was the same clock we would have been able ++ * to support a third samplerate with the UDA1341's 384FS mode. ++ * ++ * At least it would have been a minimum acceptable solution to be ++ * able to set the CPLD divisor by software. The iPAQ design is ++ * certainly a better example to follow for a new design. ++ */ ++ clk_ref = cpufreq_get(0) * 1000 / 4608; ++ if (val > clk_ref*4/7) { ++ audio_samplerate = clk_ref; ++ cfg.fs = 256; ++ clk_div = SSCR0_SerClkDiv(2); ++ } else { ++ audio_samplerate = clk_ref/2; ++ cfg.fs = 512; ++ clk_div = SSCR0_SerClkDiv(4); ++ } ++ ++ cfg.format = FMT_LSB16; ++ l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); ++ ++ Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE; ++} ++ ++/* ++ * Initialise the Assabet audio driver. ++ * ++ * Note that we have to be careful with the order that we do things here; ++ * there is a D-type flip flop which is clocked from the SFRM line which ++ * indicates whether the same is for the left or right channel to the ++ * UDA1341. ++ * ++ * When you disable the SSP (by clearing SSCR0_SSE) it appears that the ++ * SFRM signal can float high. When you re-enable the SSP, you clock the ++ * flip flop once, and end up swapping the left and right channels. ++ * ++ * The ASSABET_BCR_CODEC_RST line will force this flip flop into a known ++ * state, but this line resets other devices as well! ++ * ++ * In addition to the above, it appears that powering down the UDA1341 on ++ * early Assabets leaves the UDA_WS actively driving a logic '1' into the ++ * chip, wasting power! (you can tell this by D11 being half-on). We ++ * attempt to correct this by kicking the flip flop on init/open/close. ++ * We should probably do this on PM resume as well. ++ * ++ * (Note the ordering of ASSABET_BCR_AUDIO_ON, SFRM and ASSABET_BCR_CODEC_RST ++ * is important). ++ */ ++static void assabet_audio_init(void *dummy) ++{ ++ unsigned long flags; ++ unsigned int mdrefr; ++ ++ local_irq_save(flags); ++ ++ /* ++ * Enable the power for the UDA1341 before driving any signals. ++ * We leave the audio amp (LM4880) disabled for now. ++ */ ++ ASSABET_BCR_set(ASSABET_BCR_AUDIO_ON); ++ ++#ifdef FIX_POWER_DRAIN ++ GPSR = GPIO_SSP_SFRM; ++ GPCR = GPIO_SSP_SFRM; ++#endif ++ ++ ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); ++ ASSABET_BCR_clear(ASSABET_BCR_STEREO_LB); ++ ++ /* ++ * Setup the SSP uart. ++ */ ++ PPAR |= PPAR_SPR; ++ Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(2); ++ Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk; ++ GAFR |= GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_CLK; ++ GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; ++ GPDR &= ~(GPIO_SSP_RXD | GPIO_SSP_CLK); ++ Ser4SSCR0 |= SSCR0_SSE; ++ ++ /* ++ * Only give SFRM to the SSP after it has been enabled. ++ */ ++ GAFR |= GPIO_SSP_SFRM; ++ ++ /* ++ * The assabet board uses the SDRAM clock as the source clock for ++ * audio. This is supplied to the SA11x0 from the CPLD on pin 19. ++ * At 206MHz we need to run the audio clock (SDRAM bank 2) ++ * at half speed. This clock will scale with core frequency so ++ * the audio sample rate will also scale. The CPLD on Assabet ++ * will need to be programmed to match the core frequency. ++ */ ++ mdrefr = MDREFR; ++ if ((mdrefr & (MDREFR_K2DB2 | MDREFR_K2RUN | MDREFR_EAPD | ++ MDREFR_KAPD)) != (MDREFR_K2DB2 | MDREFR_K2RUN)) { ++ mdrefr |= MDREFR_K2DB2 | MDREFR_K2RUN; ++ mdrefr &= ~(MDREFR_EAPD | MDREFR_KAPD); ++ MDREFR = mdrefr; ++ (void) MDREFR; ++ } ++ local_irq_restore(flags); ++ ++ /* Wait for the UDA1341 to wake up */ ++ mdelay(1); ++ ++ l3_open(&uda1341); ++ ++ assabet_set_samplerate(audio_samplerate); ++ ++ /* Enable the audio power */ ++ ASSABET_BCR_clear(ASSABET_BCR_QMUTE | ASSABET_BCR_SPK_OFF); ++} ++ ++/* ++ * Shutdown the Assabet audio driver. ++ * ++ * We have to be careful about the SFRM line here for the same reasons ++ * described in the initialisation comments above. This basically means ++ * that we must hand the SSP pins back to the GPIO module before disabling ++ * the SSP. ++ * ++ * In addition, to reduce power drain, we toggle the SFRM line once so ++ * that the UDA_WS line is at logic 0. ++ * ++ * We can't clear ASSABET_BCR_CODEC_RST without knowing if the UCB1300 or ++ * ADV7171 driver is still active. If it is, then we still need to play ++ * games, so we might as well leave ASSABET_BCR_CODEC_RST set. ++ */ ++static void assabet_audio_shutdown(void *dummy) ++{ ++ ASSABET_BCR_set(ASSABET_BCR_STEREO_LB | ASSABET_BCR_QMUTE | ++ ASSABET_BCR_SPK_OFF); ++ ++ l3_close(&uda1341); ++ ++ GAFR &= ~(GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM); ++ Ser4SSCR0 = 0; ++ ++#ifdef FIX_POWER_DRAIN ++ GPSR = GPIO_SSP_SFRM; ++ GPCR = GPIO_SSP_SFRM; ++#endif ++ ++ /* disable the audio power */ ++ ASSABET_BCR_clear(ASSABET_BCR_AUDIO_ON); ++} ++ ++static int assabet_audio_ioctl( struct inode *inode, struct file *file, ++ uint cmd, ulong arg) ++{ ++ long val; ++ int ret = 0; ++ ++ /* ++ * These are platform dependent ioctls which are not handled by the ++ * generic sa1100-audio module. ++ */ ++ switch (cmd) { ++ case SNDCTL_DSP_STEREO: ++ ret = get_user(val, (int *) arg); ++ if (ret) ++ return ret; ++ /* the UDA1341 is stereo only */ ++ ret = (val == 0) ? -EINVAL : 1; ++ return put_user(ret, (int *) arg); ++ ++ case SNDCTL_DSP_CHANNELS: ++ case SOUND_PCM_READ_CHANNELS: ++ /* the UDA1341 is stereo only */ ++ return put_user(2, (long *) arg); ++ ++ case SNDCTL_DSP_SPEED: ++ ret = get_user(val, (long *) arg); ++ if (ret) break; ++ assabet_set_samplerate(val); ++ /* fall through */ ++ ++ case SOUND_PCM_READ_RATE: ++ return put_user(audio_samplerate, (long *) arg); ++ ++ case SNDCTL_DSP_SETFMT: ++ case SNDCTL_DSP_GETFMTS: ++ /* we can do signed 16-bit only */ ++ return put_user(AFMT_S16_LE, (long *) arg); ++ ++ default: ++ /* Maybe this is meant for the mixer (As per OSS Docs) */ ++ return mixer_ioctl(inode, file, cmd, arg); ++ } ++ ++ return ret; ++} ++ ++static audio_stream_t output_stream, input_stream; ++ ++static audio_state_t audio_state = { ++ output_stream: &output_stream, ++ output_dma: DMA_Ser4SSPWr, ++ output_id: "Assabet UDA1341 out", ++ input_stream: &input_stream, ++ input_dma: DMA_Ser4SSPRd, ++ input_id: "Assabet UDA1341 in", ++ need_tx_for_rx: 1, ++ hw_init: assabet_audio_init, ++ hw_shutdown: assabet_audio_shutdown, ++ client_ioctl: assabet_audio_ioctl, ++ sem: __MUTEX_INITIALIZER(audio_state.sem), ++}; ++ ++static int assabet_audio_open(struct inode *inode, struct file *file) ++{ ++ return sa1100_audio_attach(inode, file, &audio_state); ++} ++ ++/* ++ * Missing fields of this structure will be patched with the call ++ * to sa1100_audio_attach(). ++ */ ++static struct file_operations assabet_audio_fops = { ++ open: assabet_audio_open, ++ owner: THIS_MODULE ++}; ++ ++ ++static int audio_dev_id, mixer_dev_id; ++ ++static int __init assabet_uda1341_init(void) ++{ ++ int ret; ++ ++ if (!machine_is_assabet()) ++ return -ENODEV; ++ ++ ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341"); ++ if (ret) ++ goto out; ++ ++ /* register devices */ ++ audio_dev_id = register_sound_dsp(&assabet_audio_fops, -1); ++ mixer_dev_id = register_sound_mixer(&assabet_mixer_fops, -1); ++ ++#ifdef FIX_POWER_DRAIN ++ { ++ unsigned long flags; ++ local_irq_save(flags); ++ ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); ++ GPSR = GPIO_SSP_SFRM; ++ GPDR |= GPIO_SSP_SFRM; ++ GPCR = GPIO_SSP_SFRM; ++ local_irq_restore(flags); ++ } ++#endif ++ ++ printk(KERN_INFO "Sound: Assabet UDA1341: dsp id %d mixer id %d\n", ++ audio_dev_id, mixer_dev_id); ++ return 0; ++ ++release_l3: ++ l3_detach_client(&uda1341); ++out: ++ return ret; ++} ++ ++static void __exit assabet_uda1341_exit(void) ++{ ++ unregister_sound_dsp(audio_dev_id); ++ unregister_sound_mixer(mixer_dev_id); ++ l3_detach_client(&uda1341); ++} ++ ++module_init(assabet_uda1341_init); ++module_exit(assabet_uda1341_exit); ++ ++MODULE_AUTHOR("Nicolas Pitre"); ++MODULE_DESCRIPTION("Glue audio driver for the SA1110 Assabet board & Philips UDA1341 codec."); ++ ++EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/sound/h3600-uda1341.c linux-2.4.26-vrs1/drivers/sound/h3600-uda1341.c +--- linux-2.4.26/drivers/sound/h3600-uda1341.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/sound/h3600-uda1341.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,352 @@ ++/* ++ * Glue audio driver for the Compaq iPAQ H3600 & Philips UDA1341 codec. ++ * ++ * Copyright (c) 2000 Nicolas Pitre ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License. ++ * ++ * This is the machine specific part of the Compaq iPAQ (aka Bitsy) support. ++ * This driver makes use of the UDA1341 and the sa1100-audio modules. ++ * ++ * History: ++ * ++ * 2000-05-21 Nicolas Pitre Initial UDA1341 driver release. ++ * ++ * 2000-07-?? George France Bitsy support. ++ * ++ * 2000-12-13 Deborah Wallach Fixed power handling for iPAQ/h3600 ++ * ++ * 2001-06-03 Nicolas Pitre Made this file a separate module, based on ++ * the former sa1100-uda1341.c driver. ++ * ++ * 2001-07-13 Nicolas Pitre Fixes for all supported samplerates. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++//#include ++ ++#include "sa1100-audio.h" ++ ++ ++#undef DEBUG ++#ifdef DEBUG ++#define DPRINTK( x... ) printk( ##x ) ++#else ++#define DPRINTK( x... ) ++#endif ++ ++ ++#define AUDIO_NAME "Bitsy_UDA1341" ++ ++#define AUDIO_RATE_DEFAULT 44100 ++ ++ ++static struct l3_client uda1341; ++ ++static int ++mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) ++{ ++ /* ++ * We only accept mixer (type 'M') ioctls. ++ */ ++ if (_IOC_TYPE(cmd) != 'M') ++ return -EINVAL; ++ ++ return l3_command(&uda1341, cmd, (void *)arg); ++} ++ ++static struct file_operations h3600_mixer_fops = { ++ ioctl: mixer_ioctl, ++ owner: THIS_MODULE ++}; ++ ++ ++/* ++ * Audio interface ++ */ ++ ++static long audio_samplerate = AUDIO_RATE_DEFAULT; ++ ++/* ++ * Stop-gap solution until rest of hh.org HAL stuff is merged. ++ */ ++#define GPIO_H3600_CLK_SET0 GPIO_GPIO (12) ++#define GPIO_H3600_CLK_SET1 GPIO_GPIO (13) ++static void h3600_set_audio_clock(long val) ++{ ++ switch (val) { ++ case 24000: case 32000: case 48000: /* 00: 12.288 MHz */ ++ GPCR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1; ++ break; ++ ++ case 22050: case 29400: case 44100: /* 01: 11.2896 MHz */ ++ GPSR = GPIO_H3600_CLK_SET0; ++ GPCR = GPIO_H3600_CLK_SET1; ++ break; ++ ++ case 8000: case 10666: case 16000: /* 10: 4.096 MHz */ ++ GPCR = GPIO_H3600_CLK_SET0; ++ GPSR = GPIO_H3600_CLK_SET1; ++ break; ++ ++ case 10985: case 14647: case 21970: /* 11: 5.6245 MHz */ ++ GPSR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1; ++ break; ++ } ++} ++ ++static void h3600_set_samplerate(long val) ++{ ++ struct uda1341_cfg cfg; ++ int clk_div = 0; ++ ++ /* We don't want to mess with clocks when frames are in flight */ ++ Ser4SSCR0 &= ~SSCR0_SSE; ++ /* wait for any frame to complete */ ++ udelay(125); ++ ++ /* ++ * We have the following clock sources: ++ * 4.096 MHz, 5.6245 MHz, 11.2896 MHz, 12.288 MHz ++ * Those can be divided either by 256, 384 or 512. ++ * This makes up 12 combinations for the following samplerates... ++ */ ++ if (val >= 48000) ++ val = 48000; ++ else if (val >= 44100) ++ val = 44100; ++ else if (val >= 32000) ++ val = 32000; ++ else if (val >= 29400) ++ val = 29400; ++ else if (val >= 24000) ++ val = 24000; ++ else if (val >= 22050) ++ val = 22050; ++ else if (val >= 21970) ++ val = 21970; ++ else if (val >= 16000) ++ val = 16000; ++ else if (val >= 14647) ++ val = 14647; ++ else if (val >= 10985) ++ val = 10985; ++ else if (val >= 10666) ++ val = 10666; ++ else ++ val = 8000; ++ ++ /* Set the external clock generator */ ++ h3600_set_audio_clock(val); ++ ++ /* Select the clock divisor */ ++ switch (val) { ++ case 8000: ++ case 10985: ++ case 22050: ++ case 24000: ++ cfg.fs = 512; ++ clk_div = SSCR0_SerClkDiv(16); ++ break; ++ case 16000: ++ case 21970: ++ case 44100: ++ case 48000: ++ cfg.fs = 256; ++ clk_div = SSCR0_SerClkDiv(8); ++ break; ++ case 10666: ++ case 14647: ++ case 29400: ++ case 32000: ++ cfg.fs = 384; ++ clk_div = SSCR0_SerClkDiv(12); ++ break; ++ } ++ ++ cfg.format = FMT_LSB16; ++ l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); ++ Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE; ++ audio_samplerate = val; ++} ++ ++static void h3600_audio_init(void *dummy) ++{ ++ unsigned long flags; ++ ++ /* Setup the uarts */ ++ local_irq_save(flags); ++ GAFR |= (GPIO_SSP_CLK); ++ GPDR &= ~(GPIO_SSP_CLK); ++ Ser4SSCR0 = 0; ++ Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8); ++ Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk; ++ Ser4SSCR0 |= SSCR0_SSE; ++ ++ /* Enable the audio power */ ++ ++ clr_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET); ++ set_h3600_egpio(IPAQ_EGPIO_AUDIO_ON); ++ set_h3600_egpio(IPAQ_EGPIO_QMUTE); ++ local_irq_restore(flags); ++ ++ /* external clock configuration */ ++ h3600_set_samplerate(audio_samplerate); ++ ++ /* Wait for the UDA1341 to wake up */ ++ set_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET); ++ mdelay(1); ++ ++ /* make the left and right channels unswapped (flip the WS latch ) */ ++ Ser4SSDR = 0; ++ ++ /* Initialize the UDA1341 internal state */ ++ l3_open(&uda1341); ++ ++ clr_h3600_egpio(IPAQ_EGPIO_QMUTE); ++} ++ ++static void h3600_audio_shutdown(void *dummy) ++{ ++ /* disable the audio power and all signals leading to the audio chip */ ++ l3_close(&uda1341); ++ Ser4SSCR0 = 0; ++ clr_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET); ++ clr_h3600_egpio(IPAQ_EGPIO_AUDIO_ON); ++ clr_h3600_egpio(IPAQ_EGPIO_QMUTE); ++} ++ ++static int h3600_audio_ioctl(struct inode *inode, struct file *file, ++ uint cmd, ulong arg) ++{ ++ long val; ++ int ret = 0; ++ ++ /* ++ * These are platform dependent ioctls which are not handled by the ++ * generic sa1100-audio module. ++ */ ++ switch (cmd) { ++ case SNDCTL_DSP_STEREO: ++ ret = get_user(val, (int *) arg); ++ if (ret) ++ return ret; ++ /* the UDA1341 is stereo only */ ++ ret = (val == 0) ? -EINVAL : 1; ++ return put_user(ret, (int *) arg); ++ ++ case SNDCTL_DSP_CHANNELS: ++ case SOUND_PCM_READ_CHANNELS: ++ /* the UDA1341 is stereo only */ ++ return put_user(2, (long *) arg); ++ ++ case SNDCTL_DSP_SPEED: ++ ret = get_user(val, (long *) arg); ++ if (ret) break; ++ h3600_set_samplerate(val); ++ /* fall through */ ++ ++ case SOUND_PCM_READ_RATE: ++ return put_user(audio_samplerate, (long *) arg); ++ ++ case SNDCTL_DSP_SETFMT: ++ case SNDCTL_DSP_GETFMTS: ++ /* we can do 16-bit only */ ++ return put_user(AFMT_S16_LE, (long *) arg); ++ ++ default: ++ /* Maybe this is meant for the mixer (As per OSS Docs) */ ++ return mixer_ioctl(inode, file, cmd, arg); ++ } ++ ++ return ret; ++} ++ ++static audio_stream_t output_stream, input_stream; ++ ++static audio_state_t audio_state = { ++ output_stream: &output_stream, ++ output_dma: DMA_Ser4SSPWr, ++ output_id: "UDA1341 out", ++ input_stream: &input_stream, ++ input_dma: DMA_Ser4SSPRd, ++ input_id: "UDA1341 in", ++ need_tx_for_rx: 1, ++ hw_init: h3600_audio_init, ++ hw_shutdown: h3600_audio_shutdown, ++ client_ioctl: h3600_audio_ioctl, ++ sem: __MUTEX_INITIALIZER(audio_state.sem), ++}; ++ ++static int h3600_audio_open(struct inode *inode, struct file *file) ++{ ++ return sa1100_audio_attach(inode, file, &audio_state); ++} ++ ++/* ++ * Missing fields of this structure will be patched with the call ++ * to sa1100_audio_attach(). ++ */ ++static struct file_operations h3600_audio_fops = { ++ open: h3600_audio_open, ++ owner: THIS_MODULE ++}; ++ ++ ++static int audio_dev_id, mixer_dev_id; ++ ++static int __init h3600_uda1341_init(void) ++{ ++ int ret; ++ ++ if (!machine_is_h3xxx()) ++ return -ENODEV; ++ ++ ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341"); ++ if (ret) ++ goto out; ++ ++ /* register devices */ ++ audio_dev_id = register_sound_dsp(&h3600_audio_fops, -1); ++ mixer_dev_id = register_sound_mixer(&h3600_mixer_fops, -1); ++ ++ printk( KERN_INFO "iPAQ audio support initialized\n" ); ++ return 0; ++ ++release_l3: ++ l3_detach_client(&uda1341); ++out: ++ return ret; ++} ++ ++static void __exit h3600_uda1341_exit(void) ++{ ++ unregister_sound_dsp(audio_dev_id); ++ unregister_sound_mixer(mixer_dev_id); ++ l3_detach_client(&uda1341); ++} ++ ++module_init(h3600_uda1341_init); ++module_exit(h3600_uda1341_exit); ++ ++MODULE_AUTHOR("Nicolas Pitre, George France"); ++MODULE_DESCRIPTION("Glue audio driver for the Compaq iPAQ H3600 & Philips UDA1341 codec."); ++ ++EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/sound/pangolin-uda1341.c linux-2.4.26-vrs1/drivers/sound/pangolin-uda1341.c +--- linux-2.4.26/drivers/sound/pangolin-uda1341.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/sound/pangolin-uda1341.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,322 @@ ++/* ++ * Glue audio driver for the SA1110 Pangolin board & Philips UDA1341 codec. ++ * ++ * Copyright (c) 2000 Nicolas Pitre ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License. ++ * ++ * This is the machine specific part of the Pangolin/UDA1341 support. ++ * This driver makes use of the UDA1341 and the sa1100-audio modules. ++ * ++ * History: ++ * ++ * 2000-05-21 Nicolas Pitre Initial release. ++ * ++ * 2001-06-03 Nicolas Pitre Made this file a separate module, based on ++ * the former sa1100-uda1341.c driver. ++ * ++ * 2001-07-17 Nicolas Pitre Supports 44100Hz and 22050Hz samplerate now. ++ * ++ * 2001-08-06 Richard Fan Pangolin Support ++ * ++ * 2001-09-23 Russell King Update inline with Assabet driver ++ * Remove old L3 bus driver ++ * ++ * Note: this should probably be merged with the Assabet audio driver, ++ * and become the "SDRAM-clock driven" SA1100 audio driver. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "sa1100-audio.h" ++ ++/* ++ * Debugging? ++ */ ++#undef DEBUG ++ ++ ++#ifdef DEBUG ++#define DPRINTK( x... ) printk( ##x ) ++#else ++#define DPRINTK( x... ) ++#endif ++ ++ ++#define AUDIO_RATE_DEFAULT 44100 ++ ++#define QmutePin GPIO_GPIO(4) ++#define SpeakerOffPin GPIO_GPIO(5) ++ ++/* ++ * Mixer (UDA1341) interface ++ */ ++ ++static struct l3_client uda1341; ++ ++static int ++mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) ++{ ++ /* ++ * We only accept mixer (type 'M') ioctls. ++ */ ++ if (_IOC_TYPE(cmd) != 'M') ++ return -EINVAL; ++ ++ return l3_command(&uda1341, cmd, (void *)arg); ++} ++ ++static struct file_operations pangolin_mixer_fops = { ++ ioctl: mixer_ioctl, ++ owner: THIS_MODULE ++}; ++ ++ ++/* ++ * Audio interface ++ */ ++static long audio_samplerate = AUDIO_RATE_DEFAULT; ++ ++static void pangolin_set_samplerate(long val) ++{ ++ struct uda1341_cfg cfg; ++ int clk_div; ++ ++ /* We don't want to mess with clocks when frames are in flight */ ++ Ser4SSCR0 &= ~SSCR0_SSE; ++ /* wait for any frame to complete */ ++ udelay(125); ++ ++ /* ++ * Our clock source is derived from the CPLD on which we don't have ++ * much control unfortunately. This was intended for a fixed 44100Hz ++ * samplerate assuming a core clock of 206 MHz. Still we can play ++ * with the SA1110's clock divisor for the SSP port to get a 22050Hz ++ * samplerate. ++ * ++ * Apparently the clock sent to the SA1110 for the SSP port is ++ * divided from the clock sent to the UDA1341 (some people tried to ++ * be too clever in their design, or simply failed to read the SA1110 ++ * manual). If it was the same source we would have been able to ++ * support a third samplerate. ++ * ++ * At least it would have been a minimum acceptable solution to be ++ * able to set the CPLD divisor by software. The iPAQ design is ++ * certainly a better example to follow for a new design. ++ */ ++ if (val >= 44100) { ++ audio_samplerate = 44100; ++ cfg.fs = 256; ++ clk_div = SSCR0_SerClkDiv(2); ++ } else { ++ audio_samplerate = 22050; ++ cfg.fs = 512; ++ clk_div = SSCR0_SerClkDiv(4); ++ } ++ ++ cfg.format = FMT_LSB16; ++ l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); ++ ++ Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE; ++} ++ ++static void pangolin_audio_init(void *dummy) ++{ ++ unsigned long flags; ++ unsigned int mdrefr; ++ ++ local_irq_save(flags); ++ ++ /* ++ * Setup the SSP uart. ++ */ ++ PPAR |= PPAR_SPR; ++ Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(2); ++ Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk; ++ GAFR |= GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_CLK | ++ GPIO_SSP_SFRM; ++ GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; ++ GPDR &= ~(GPIO_SSP_RXD | GPIO_SSP_CLK); ++ Ser4SSCR0 |= SSCR0_SSE; ++ ++ GAFR &= ~(SpeakerOffPin | QmutePin); ++ GPDR |= (SpeakerOffPin | QmutePin); ++ GPCR = SpeakerOffPin; ++ ++ /* ++ * The assabet board uses the SDRAM clock as the source clock for ++ * audio. This is supplied to the SA11x0 from the CPLD on pin 19. ++ * At 206MHz we need to run the audio clock (SDRAM bank 2) ++ * at half speed. This clock will scale with core frequency so ++ * the audio sample rate will also scale. The CPLD on Assabet ++ * will need to be programmed to match the core frequency. ++ */ ++ mdrefr = MDREFR; ++ if ((mdrefr & (MDREFR_K2DB2 | MDREFR_K2RUN | MDREFR_EAPD | ++ MDREFR_KAPD)) != (MDREFR_K2DB2 | MDREFR_K2RUN)) { ++ mdrefr |= MDREFR_K2DB2 | MDREFR_K2RUN; ++ mdrefr &= ~(MDREFR_EAPD | MDREFR_KAPD); ++ MDREFR = mdrefr; ++ (void) MDREFR; ++ } ++ local_irq_restore(flags); ++ ++ /* Wait for the UDA1341 to wake up */ ++ mdelay(100); ++ ++ l3_open(&uda1341); ++ ++ pangolin_set_samplerate(audio_samplerate); ++ ++ GPCR = QmutePin; ++} ++ ++static void pangolin_audio_shutdown(void *dummy) ++{ ++ GPSR = QmutePin; ++ ++ l3_close(&uda1341); ++ ++ Ser4SSCR0 = 0; ++ MDREFR &= ~(MDREFR_K2DB2 | MDREFR_K2RUN); ++} ++ ++static int pangolin_audio_ioctl( struct inode *inode, struct file *file, ++ uint cmd, ulong arg) ++{ ++ long val; ++ int ret = 0; ++ ++ /* ++ * These are platform dependent ioctls which are not handled by the ++ * generic sa1100-audio module. ++ */ ++ switch (cmd) { ++ case SNDCTL_DSP_STEREO: ++ ret = get_user(val, (int *) arg); ++ if (ret) ++ return ret; ++ /* the UDA1341 is stereo only */ ++ ret = (val == 0) ? -EINVAL : 1; ++ return put_user(ret, (int *) arg); ++ ++ case SNDCTL_DSP_CHANNELS: ++ case SOUND_PCM_READ_CHANNELS: ++ /* the UDA1341 is stereo only */ ++ return put_user(2, (long *) arg); ++ ++ case SNDCTL_DSP_SPEED: ++ ret = get_user(val, (long *) arg); ++ if (ret) break; ++ pangolin_set_samplerate(val); ++ /* fall through */ ++ ++ case SOUND_PCM_READ_RATE: ++ return put_user(audio_samplerate, (long *) arg); ++ ++ case SNDCTL_DSP_SETFMT: ++ case SNDCTL_DSP_GETFMTS: ++ /* we can do signed 16-bit only */ ++ return put_user(AFMT_S16_LE, (long *) arg); ++ ++ default: ++ /* Maybe this is meant for the mixer (As per OSS Docs) */ ++ return mixer_ioctl(inode, file, cmd, arg); ++ } ++ ++ return ret; ++} ++ ++static audio_stream_t output_stream, input_stream; ++ ++static audio_state_t audio_state = { ++ output_stream: &output_stream, ++ output_dma: DMA_Ser4SSPWr, ++ output_id: "Pangolin UDA1341 out", ++ input_stream: &input_stream, ++ input_dma: DMA_Ser4SSPRd, ++ input_id: "Pangolin UDA1341 in", ++ need_tx_for_rx: 1, ++ hw_init: pangolin_audio_init, ++ hw_shutdown: pangolin_audio_shutdown, ++ client_ioctl: pangolin_audio_ioctl, ++ sem: __MUTEX_INITIALIZER(audio_state.sem), ++}; ++ ++static int pangolin_audio_open(struct inode *inode, struct file *file) ++{ ++ return sa1100_audio_attach(inode, file, &audio_state); ++} ++ ++/* ++ * Missing fields of this structure will be patched with the call ++ * to sa1100_audio_attach(). ++ */ ++static struct file_operations pangolin_audio_fops = { ++ open: pangolin_audio_open, ++ owner: THIS_MODULE ++}; ++ ++ ++static int audio_dev_id, mixer_dev_id; ++ ++static int __init pangolin_uda1341_init(void) ++{ ++ unsigned long flags; ++ int ret; ++ ++ if (!machine_is_pangolin()) ++ return -ENODEV; ++ ++ ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341"); ++ if (ret) ++ goto out; ++ ++ /* register devices */ ++ audio_dev_id = register_sound_dsp(&pangolin_audio_fops, -1); ++ mixer_dev_id = register_sound_mixer(&pangolin_mixer_fops, -1); ++ ++ local_irq_save(flags); ++ GAFR &= ~(SpeakerOffPin | QmutePin); ++ GPDR |= (SpeakerOffPin | QmutePin); ++ local_irq_restore(flags); ++ ++ printk(KERN_INFO "Pangolin UDA1341 audio driver initialized\n"); ++ return 0; ++ ++release_l3: ++ l3_detach_client(&uda1341); ++out: ++ return ret; ++} ++ ++static void __exit pangolin_uda1341_exit(void) ++{ ++ unregister_sound_dsp(audio_dev_id); ++ unregister_sound_mixer(mixer_dev_id); ++ l3_detach_client(&uda1341); ++} ++ ++module_init(pangolin_uda1341_init); ++module_exit(pangolin_uda1341_exit); ++ ++MODULE_AUTHOR("Nicolas Pitre"); ++MODULE_DESCRIPTION("Glue audio driver for the SA1110 Pangolin board & Philips UDA1341 codec."); ++ ++EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/sound/sa1100-audio.c linux-2.4.26-vrs1/drivers/sound/sa1100-audio.c +--- linux-2.4.26/drivers/sound/sa1100-audio.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/sound/sa1100-audio.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,976 @@ ++/* ++ * Common audio handling for the SA11x0 processor ++ * ++ * Copyright (C) 2000, 2001 Nicolas Pitre ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License. ++ * ++ * ++ * This module handles the generic buffering/DMA/mmap audio interface for ++ * codecs connected to the SA1100 chip. All features depending on specific ++ * hardware implementations like supported audio formats or samplerates are ++ * relegated to separate specific modules. ++ * ++ * ++ * History: ++ * ++ * 2000-05-21 Nicolas Pitre Initial release. ++ * ++ * 2000-06-10 Erik Bunce Add initial poll support. ++ * ++ * 2000-08-22 Nicolas Pitre Removed all DMA stuff. Now using the ++ * generic SA1100 DMA interface. ++ * ++ * 2000-11-30 Nicolas Pitre - Validation of opened instances; ++ * - Power handling at open/release time instead ++ * of driver load/unload; ++ * ++ * 2001-06-03 Nicolas Pitre Made this file a separate module, based on ++ * the former sa1100-uda1341.c driver. ++ * ++ * 2001-07-22 Nicolas Pitre - added mmap() and realtime support ++ * - corrected many details to better comply ++ * with the OSS API ++ * ++ * 2001-10-19 Nicolas Pitre - brought DMA registration processing ++ * into this module for better ressource ++ * management. This also fixes a bug ++ * with the suspend/resume logic. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "sa1100-audio.h" ++ ++ ++#undef DEBUG ++/* #define DEBUG 1 */ ++#ifdef DEBUG ++#define DPRINTK( x... ) printk( ##x ) ++#else ++#define DPRINTK( x... ) ++#endif ++ ++ ++#define AUDIO_NAME "sa1100-audio" ++#define AUDIO_NBFRAGS_DEFAULT 8 ++#define AUDIO_FRAGSIZE_DEFAULT 8192 ++ ++#define NEXT_BUF(_s_,_b_) { \ ++ (_s_)->_b_##_idx++; \ ++ (_s_)->_b_##_idx %= (_s_)->nbfrags; \ ++ (_s_)->_b_ = (_s_)->buffers + (_s_)->_b_##_idx; } ++ ++#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref) ++ ++/* ++ * This function frees all buffers ++ */ ++ ++static void audio_clear_buf(audio_stream_t * s) ++{ ++ DPRINTK("audio_clear_buf\n"); ++ ++ /* ensure DMA won't run anymore */ ++ s->active = 0; ++ s->stopped = 0; ++ sa1100_dma_flush_all(s->dma_ch); ++ ++ if (s->buffers) { ++ int frag; ++ for (frag = 0; frag < s->nbfrags; frag++) { ++ if (!s->buffers[frag].master) ++ continue; ++ consistent_free(s->buffers[frag].start, ++ s->buffers[frag].master, ++ s->buffers[frag].dma_addr); ++ } ++ kfree(s->buffers); ++ s->buffers = NULL; ++ } ++ ++ s->buf_idx = 0; ++ s->buf = NULL; ++} ++ ++ ++/* ++ * This function allocates the buffer structure array and buffer data space ++ * according to the current number of fragments and fragment size. ++ */ ++ ++static int audio_setup_buf(audio_stream_t * s) ++{ ++ int frag; ++ int dmasize = 0; ++ char *dmabuf = NULL; ++ dma_addr_t dmaphys = 0; ++ ++ if (s->buffers) ++ return -EBUSY; ++ ++ s->buffers = (audio_buf_t *) ++ kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL); ++ if (!s->buffers) ++ goto err; ++ memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags); ++ ++ for (frag = 0; frag < s->nbfrags; frag++) { ++ audio_buf_t *b = &s->buffers[frag]; ++ ++ /* ++ * Let's allocate non-cached memory for DMA buffers. ++ * We try to allocate all memory at once. ++ * If this fails (a common reason is memory fragmentation), ++ * then we allocate more smaller buffers. ++ */ ++ if (!dmasize) { ++ dmasize = (s->nbfrags - frag) * s->fragsize; ++ do { ++ dmabuf = consistent_alloc(GFP_KERNEL|GFP_DMA, ++ dmasize, ++ &dmaphys); ++ if (!dmabuf) ++ dmasize -= s->fragsize; ++ } while (!dmabuf && dmasize); ++ if (!dmabuf) ++ goto err; ++ b->master = dmasize; ++ memzero(dmabuf, dmasize); ++ } ++ ++ b->start = dmabuf; ++ b->dma_addr = dmaphys; ++ b->stream = s; ++ sema_init(&b->sem, 1); ++ DPRINTK("buf %d: start %p dma %p\n", frag, b->start, ++ b->dma_addr); ++ ++ dmabuf += s->fragsize; ++ dmaphys += s->fragsize; ++ dmasize -= s->fragsize; ++ } ++ ++ s->buf_idx = 0; ++ s->buf = &s->buffers[0]; ++ s->bytecount = 0; ++ s->getptrCount = 0; ++ s->fragcount = 0; ++ ++ return 0; ++ ++err: ++ printk(AUDIO_NAME ": unable to allocate audio memory\n "); ++ audio_clear_buf(s); ++ return -ENOMEM; ++} ++ ++ ++/* ++ * This function yanks all buffers from the DMA code's control and ++ * resets them ready to be used again. ++ */ ++ ++static void audio_reset_buf(audio_stream_t * s) ++{ ++ int frag; ++ ++ s->active = 0; ++ s->stopped = 0; ++ sa1100_dma_flush_all(s->dma_ch); ++ if (s->buffers) { ++ for (frag = 0; frag < s->nbfrags; frag++) { ++ audio_buf_t *b = &s->buffers[frag]; ++ b->size = 0; ++ sema_init(&b->sem, 1); ++ } ++ } ++ s->bytecount = 0; ++ s->getptrCount = 0; ++ s->fragcount = 0; ++} ++ ++ ++/* ++ * DMA callback functions ++ */ ++ ++static void audio_dmaout_done_callback(void *buf_id, int size) ++{ ++ audio_buf_t *b = (audio_buf_t *) buf_id; ++ audio_stream_t *s = b->stream; ++ ++ /* Accounting */ ++ s->bytecount += size; ++ s->fragcount++; ++ ++ /* Recycle buffer */ ++ if (s->mapped) ++ sa1100_dma_queue_buffer(s->dma_ch, buf_id, ++ b->dma_addr, s->fragsize); ++ else ++ up(&b->sem); ++ ++ /* And any process polling on write. */ ++ wake_up(&s->wq); ++} ++ ++static void audio_dmain_done_callback(void *buf_id, int size) ++{ ++ audio_buf_t *b = (audio_buf_t *) buf_id; ++ audio_stream_t *s = b->stream; ++ ++ /* Accounting */ ++ s->bytecount += size; ++ s->fragcount++; ++ ++ /* Recycle buffer */ ++ if (s->mapped) { ++ sa1100_dma_queue_buffer(s->dma_ch, buf_id, ++ b->dma_addr, s->fragsize); ++ } else { ++ b->size = size; ++ up(&b->sem); ++ } ++ ++ /* And any process polling on write. */ ++ wake_up(&s->wq); ++} ++ ++static int audio_sync(struct file *file) ++{ ++ audio_state_t *state = (audio_state_t *)file->private_data; ++ audio_stream_t *s = state->output_stream; ++ audio_buf_t *b; ++ ++ DPRINTK("audio_sync\n"); ++ ++ if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped) ++ return 0; ++ ++ /* ++ * Send current buffer if it contains data. Be sure to send ++ * a full sample count. ++ */ ++ b = s->buf; ++ if (b->size &= ~3) { ++ down(&b->sem); ++ sa1100_dma_queue_buffer(s->dma_ch, (void *) b, ++ b->dma_addr, b->size); ++ b->size = 0; ++ NEXT_BUF(s, buf); ++ } ++ ++ /* ++ * Let's wait for the last buffer we sent i.e. the one before the ++ * current buf_idx. When we acquire the semaphore, this means either: ++ * - DMA on the buffer completed or ++ * - the buffer was already free thus nothing else to sync. ++ */ ++ b = s->buffers + ((s->nbfrags + s->buf_idx - 1) % s->nbfrags); ++ if (down_interruptible(&b->sem)) ++ return -EINTR; ++ up(&b->sem); ++ return 0; ++} ++ ++ ++static int audio_write(struct file *file, const char *buffer, ++ size_t count, loff_t * ppos) ++{ ++ const char *buffer0 = buffer; ++ audio_state_t *state = (audio_state_t *)file->private_data; ++ audio_stream_t *s = state->output_stream; ++ int chunksize, ret = 0; ++ ++ DPRINTK("audio_write: count=%d\n", count); ++ ++ if (ppos != &file->f_pos) ++ return -ESPIPE; ++ if (s->mapped) ++ return -ENXIO; ++ if (!s->buffers && audio_setup_buf(s)) ++ return -ENOMEM; ++ ++ while (count > 0) { ++ audio_buf_t *b = s->buf; ++ ++ /* Wait for a buffer to become free */ ++ if (file->f_flags & O_NONBLOCK) { ++ ret = -EAGAIN; ++ if (down_trylock(&b->sem)) ++ break; ++ } else { ++ ret = -ERESTARTSYS; ++ if (down_interruptible(&b->sem)) ++ break; ++ } ++ ++ /* Feed the current buffer */ ++ chunksize = s->fragsize - b->size; ++ if (chunksize > count) ++ chunksize = count; ++ DPRINTK("write %d to %d\n", chunksize, s->buf_idx); ++ if (copy_from_user(b->start + b->size, buffer, chunksize)) { ++ up(&b->sem); ++ return -EFAULT; ++ } ++ b->size += chunksize; ++ buffer += chunksize; ++ count -= chunksize; ++ if (b->size < s->fragsize) { ++ up(&b->sem); ++ break; ++ } ++ ++ /* Send current buffer to dma */ ++ s->active = 1; ++ sa1100_dma_queue_buffer(s->dma_ch, (void *) b, ++ b->dma_addr, b->size); ++ b->size = 0; /* indicate that the buffer has been sent */ ++ NEXT_BUF(s, buf); ++ } ++ ++ if ((buffer - buffer0)) ++ ret = buffer - buffer0; ++ DPRINTK("audio_write: return=%d\n", ret); ++ return ret; ++} ++ ++ ++static inline void audio_check_tx_spin(audio_state_t *state) ++{ ++ /* ++ * With some codecs like the Philips UDA1341 we must ensure ++ * there is an output stream at any time while recording since ++ * this is how the UDA1341 gets its clock from the SA1100. ++ * So while there is no playback data to send, the output DMA ++ * will spin with all zeroes. We use the cache flush special ++ * area for that. ++ */ ++ if (state->need_tx_for_rx && !state->tx_spinning) { ++ sa1100_dma_set_spin(state->output_stream->dma_ch, ++ (dma_addr_t)FLUSH_BASE_PHYS, 2048); ++ state->tx_spinning = 1; ++ } ++} ++ ++ ++static void audio_prime_dma(audio_stream_t *s) ++{ ++ int i; ++ ++ s->active = 1; ++ for (i = 0; i < s->nbfrags; i++) { ++ audio_buf_t *b = s->buf; ++ down(&b->sem); ++ sa1100_dma_queue_buffer(s->dma_ch, (void *) b, ++ b->dma_addr, s->fragsize); ++ NEXT_BUF(s, buf); ++ } ++} ++ ++ ++static int audio_read(struct file *file, char *buffer, ++ size_t count, loff_t * ppos) ++{ ++ char *buffer0 = buffer; ++ audio_state_t *state = (audio_state_t *)file->private_data; ++ audio_stream_t *s = state->input_stream; ++ int chunksize, ret = 0; ++ ++ DPRINTK("audio_read: count=%d\n", count); ++ ++ if (ppos != &file->f_pos) ++ return -ESPIPE; ++ if (s->mapped) ++ return -ENXIO; ++ ++ if (!s->active) { ++ if (!s->buffers && audio_setup_buf(s)) ++ return -ENOMEM; ++ audio_check_tx_spin(state); ++ audio_prime_dma(s); ++ } ++ ++ while (count > 0) { ++ audio_buf_t *b = s->buf; ++ ++ /* Wait for a buffer to become full */ ++ if (file->f_flags & O_NONBLOCK) { ++ ret = -EAGAIN; ++ if (down_trylock(&b->sem)) ++ break; ++ } else { ++ ret = -ERESTARTSYS; ++ if (down_interruptible(&b->sem)) ++ break; ++ } ++ ++ /* Grab data from the current buffer */ ++ chunksize = b->size; ++ if (chunksize > count) ++ chunksize = count; ++ DPRINTK("read %d from %d\n", chunksize, s->buf_idx); ++ if (copy_to_user(buffer, ++ b->start + s->fragsize - b->size, ++ chunksize)) { ++ up(&b->sem); ++ return -EFAULT; ++ } ++ b->size -= chunksize; ++ buffer += chunksize; ++ count -= chunksize; ++ if (b->size > 0) { ++ up(&b->sem); ++ break; ++ } ++ ++ /* Make current buffer available for DMA again */ ++ sa1100_dma_queue_buffer(s->dma_ch, (void *) b, ++ b->dma_addr, s->fragsize); ++ NEXT_BUF(s, buf); ++ } ++ ++ if ((buffer - buffer0)) ++ ret = buffer - buffer0; ++ DPRINTK("audio_read: return=%d\n", ret); ++ return ret; ++} ++ ++ ++static int audio_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ audio_state_t *state = (audio_state_t *)file->private_data; ++ audio_stream_t *s; ++ unsigned long size, vma_addr; ++ int i, ret; ++ ++ if (vma->vm_pgoff != 0) ++ return -EINVAL; ++ ++ if (vma->vm_flags & VM_WRITE) { ++ if (!state->wr_ref) ++ return -EINVAL;; ++ s = state->output_stream; ++ } else if (vma->vm_flags & VM_READ) { ++ if (!state->rd_ref) ++ return -EINVAL; ++ s = state->input_stream; ++ } else return -EINVAL; ++ ++ if (s->mapped) ++ return -EINVAL; ++ size = vma->vm_end - vma->vm_start; ++ if (size != s->fragsize * s->nbfrags) ++ return -EINVAL; ++ if (!s->buffers && audio_setup_buf(s)) ++ return -ENOMEM; ++ vma_addr = vma->vm_start; ++ for (i = 0; i < s->nbfrags; i++) { ++ audio_buf_t *buf = &s->buffers[i]; ++ if (!buf->master) ++ continue; ++ ret = remap_page_range(vma_addr, buf->dma_addr, buf->master, vma->vm_page_prot); ++ if (ret) ++ return ret; ++ vma_addr += buf->master; ++ } ++ s->mapped = 1; ++ ++ return 0; ++} ++ ++ ++static unsigned int audio_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ audio_state_t *state = (audio_state_t *)file->private_data; ++ audio_stream_t *is = state->input_stream; ++ audio_stream_t *os = state->output_stream; ++ unsigned int mask = 0; ++ int i; ++ ++ DPRINTK("audio_poll(): mode=%s%s\n", ++ (file->f_mode & FMODE_READ) ? "r" : "", ++ (file->f_mode & FMODE_WRITE) ? "w" : ""); ++ ++ if (file->f_mode & FMODE_READ) { ++ /* Start audio input if not already active */ ++ if (!is->active) { ++ if (!is->buffers && audio_setup_buf(is)) ++ return -ENOMEM; ++ audio_check_tx_spin(state); ++ audio_prime_dma(is); ++ } ++ poll_wait(file, &is->wq, wait); ++ } ++ ++ if (file->f_mode & FMODE_WRITE) { ++ if (!os->buffers && audio_setup_buf(os)) ++ return -ENOMEM; ++ poll_wait(file, &os->wq, wait); ++ } ++ ++ if (file->f_mode & FMODE_READ) { ++ if (is->mapped) { ++/* if the buffer is mapped assume we care that there are more bytes available than ++ when we last asked using SNDCTL_DSP_GETxPTR */ ++ if (is->bytecount != is->getptrCount) ++ mask |= POLLIN | POLLRDNORM; ++ } else { ++ for (i = 0; i < is->nbfrags; i++) { ++ if (atomic_read(&is->buffers[i].sem.count) > 0) { ++ mask |= POLLIN | POLLRDNORM; ++ break; ++ } ++ } ++ } ++ } ++ if (file->f_mode & FMODE_WRITE) { ++ if (os->mapped) { ++ if (os->bytecount != os->getptrCount) ++ mask |= POLLOUT | POLLWRNORM; ++ } else { ++ for (i = 0; i < os->nbfrags; i++) { ++ if (atomic_read(&os->buffers[i].sem.count) > 0) { ++ mask |= POLLOUT | POLLWRNORM; ++ break; ++ } ++ } ++ } ++ } ++ ++ DPRINTK("audio_poll() returned mask of %s%s\n", ++ (mask & POLLIN) ? "r" : "", ++ (mask & POLLOUT) ? "w" : ""); ++ ++ return mask; ++} ++ ++ ++static loff_t audio_llseek(struct file *file, loff_t offset, int origin) ++{ ++ return -ESPIPE; ++} ++ ++ ++static int audio_set_fragments(audio_stream_t *s, int val) ++{ ++ if (s->active) ++ return -EBUSY; ++ if (s->buffers) ++ audio_clear_buf(s); ++ s->nbfrags = (val >> 16) & 0x7FFF; ++ val &= 0xffff; ++ if (val < 4) ++ val = 4; ++ if (val > 15) ++ val = 15; ++ s->fragsize = 1 << val; ++ if (s->nbfrags < 2) ++ s->nbfrags = 2; ++ if (s->nbfrags * s->fragsize > 128 * 1024) ++ s->nbfrags = 128 * 1024 / s->fragsize; ++ if (audio_setup_buf(s)) ++ return -ENOMEM; ++ return val|(s->nbfrags << 16); ++} ++ ++static int audio_ioctl(struct inode *inode, struct file *file, ++ uint cmd, ulong arg) ++{ ++ audio_state_t *state = (audio_state_t *)file->private_data; ++ audio_stream_t *os = state->output_stream; ++ audio_stream_t *is = state->input_stream; ++ long val; ++ ++ /* dispatch based on command */ ++ switch (cmd) { ++ case OSS_GETVERSION: ++ return put_user(SOUND_VERSION, (int *)arg); ++ ++ case SNDCTL_DSP_GETBLKSIZE: ++ if (file->f_mode & FMODE_WRITE) ++ return put_user(os->fragsize, (int *)arg); ++ else ++ return put_user(is->fragsize, (int *)arg); ++ ++ case SNDCTL_DSP_GETCAPS: ++ val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP; ++ if (is && os) ++ val |= DSP_CAP_DUPLEX; ++ return put_user(val, (int *)arg); ++ ++ case SNDCTL_DSP_SETFRAGMENT: ++ if (get_user(val, (long *) arg)) ++ return -EFAULT; ++ if (file->f_mode & FMODE_READ) { ++ int ret = audio_set_fragments(is, val); ++ if (ret < 0) ++ return ret; ++ ret = put_user(ret, (int *)arg); ++ if (ret) ++ return ret; ++ } ++ if (file->f_mode & FMODE_WRITE) { ++ int ret = audio_set_fragments(os, val); ++ if (ret < 0) ++ return ret; ++ ret = put_user(ret, (int *)arg); ++ if (ret) ++ return ret; ++ } ++ return 0; ++ ++ case SNDCTL_DSP_SYNC: ++ return audio_sync(file); ++ ++ case SNDCTL_DSP_SETDUPLEX: ++ return 0; ++ ++ case SNDCTL_DSP_POST: ++ return 0; ++ ++ case SNDCTL_DSP_GETTRIGGER: ++ val = 0; ++ if (file->f_mode & FMODE_READ && is->active && !is->stopped) ++ val |= PCM_ENABLE_INPUT; ++ if (file->f_mode & FMODE_WRITE && os->active && !os->stopped) ++ val |= PCM_ENABLE_OUTPUT; ++ return put_user(val, (int *)arg); ++ ++ case SNDCTL_DSP_SETTRIGGER: ++ if (get_user(val, (int *)arg)) ++ return -EFAULT; ++ if (file->f_mode & FMODE_READ) { ++ if (val & PCM_ENABLE_INPUT) { ++ if (!is->active) { ++ if (!is->buffers && audio_setup_buf(is)) ++ return -ENOMEM; ++ audio_prime_dma(is); ++ } ++ audio_check_tx_spin(state); ++ if (is->stopped) { ++ is->stopped = 0; ++ sa1100_dma_resume(is->dma_ch); ++ } ++ } else { ++ sa1100_dma_stop(is->dma_ch); ++ is->stopped = 1; ++ } ++ } ++ if (file->f_mode & FMODE_WRITE) { ++ if (val & PCM_ENABLE_OUTPUT) { ++ if (!os->active) { ++ if (!os->buffers && audio_setup_buf(os)) ++ return -ENOMEM; ++ if (os->mapped) ++ audio_prime_dma(os); ++ } ++ if (os->stopped) { ++ os->stopped = 0; ++ sa1100_dma_resume(os->dma_ch); ++ } ++ } else { ++ sa1100_dma_stop(os->dma_ch); ++ os->stopped = 1; ++ } ++ } ++ return 0; ++ ++ case SNDCTL_DSP_GETOPTR: ++ case SNDCTL_DSP_GETIPTR: ++ { ++ count_info inf = { 0, }; ++ audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is; ++ audio_buf_t *b; ++ dma_addr_t ptr; ++ int bytecount, offset, flags; ++ ++ if ((s == is && !(file->f_mode & FMODE_READ)) || ++ (s == os && !(file->f_mode & FMODE_WRITE))) ++ return -EINVAL; ++ if (s->active) { ++ save_flags_cli(flags); ++ if (sa1100_dma_get_current(s->dma_ch, (void *)&b, &ptr) == 0) { ++ offset = ptr - b->dma_addr; ++ inf.ptr = (b - s->buffers) * s->fragsize + offset; ++ } else offset = 0; ++ bytecount = s->bytecount + offset; ++ s->getptrCount = s->bytecount; /* so poll can tell if it changes */ ++ inf.blocks = s->fragcount; ++ s->fragcount = 0; ++ restore_flags(flags); ++ if (bytecount < 0) ++ bytecount = 0; ++ inf.bytes = bytecount; ++ } ++ return copy_to_user((void *)arg, &inf, sizeof(inf)); ++ } ++ ++ case SNDCTL_DSP_GETOSPACE: ++ { ++ audio_buf_info inf = { 0, }; ++ int i; ++ ++ if (!(file->f_mode & FMODE_WRITE)) ++ return -EINVAL; ++ if (!os->buffers && audio_setup_buf(os)) ++ return -ENOMEM; ++ for (i = 0; i < os->nbfrags; i++) { ++ if (atomic_read(&os->buffers[i].sem.count) > 0) { ++ if (os->buffers[i].size == 0) ++ inf.fragments++; ++ inf.bytes += os->fragsize - os->buffers[i].size; ++ } ++ } ++ inf.fragstotal = os->nbfrags; ++ inf.fragsize = os->fragsize; ++ return copy_to_user((void *)arg, &inf, sizeof(inf)); ++ } ++ ++ case SNDCTL_DSP_GETISPACE: ++ { ++ audio_buf_info inf = { 0, }; ++ int i; ++ ++ if (!(file->f_mode & FMODE_READ)) ++ return -EINVAL; ++ if (!is->buffers && audio_setup_buf(is)) ++ return -ENOMEM; ++ for (i = 0; i < is->nbfrags; i++) { ++ if (atomic_read(&is->buffers[i].sem.count) > 0) { ++ if (is->buffers[i].size == is->fragsize) ++ inf.fragments++; ++ inf.bytes += is->buffers[i].size; ++ } ++ } ++ inf.fragstotal = is->nbfrags; ++ inf.fragsize = is->fragsize; ++ return copy_to_user((void *)arg, &inf, sizeof(inf)); ++ } ++ ++ case SNDCTL_DSP_NONBLOCK: ++ file->f_flags |= O_NONBLOCK; ++ return 0; ++ ++ case SNDCTL_DSP_RESET: ++ if (file->f_mode & FMODE_READ) { ++ if (state->tx_spinning) { ++ sa1100_dma_set_spin(os->dma_ch, 0, 0); ++ state->tx_spinning = 0; ++ } ++ audio_reset_buf(is); ++ } ++ if (file->f_mode & FMODE_WRITE) { ++ audio_reset_buf(os); ++ } ++ return 0; ++ ++ default: ++ /* ++ * Let the client of this module handle the ++ * non generic ioctls ++ */ ++ return state->client_ioctl(inode, file, cmd, arg); ++ } ++ ++ return 0; ++} ++ ++ ++static int audio_release(struct inode *inode, struct file *file) ++{ ++ audio_state_t *state = (audio_state_t *)file->private_data; ++ DPRINTK("audio_release\n"); ++ ++ down(&state->sem); ++ ++ if (file->f_mode & FMODE_READ) { ++ if (state->tx_spinning) { ++ sa1100_dma_set_spin(state->output_stream->dma_ch, 0, 0); ++ state->tx_spinning = 0; ++ } ++ audio_clear_buf(state->input_stream); ++ if (!state->skip_dma_init) { ++ sa1100_free_dma(state->input_stream->dma_ch); ++ if (state->need_tx_for_rx && !state->wr_ref) ++ sa1100_free_dma(state->output_stream->dma_ch); ++ } ++ state->rd_ref = 0; ++ } ++ ++ if (file->f_mode & FMODE_WRITE) { ++ audio_sync(file); ++ audio_clear_buf(state->output_stream); ++ if (!state->skip_dma_init) ++ if (!state->need_tx_for_rx || !state->rd_ref) ++ sa1100_free_dma(state->output_stream->dma_ch); ++ state->wr_ref = 0; ++ } ++ ++ if (!AUDIO_ACTIVE(state)) { ++ if (state->hw_shutdown) ++ state->hw_shutdown(state->data); ++#ifdef CONFIG_PM ++ pm_unregister(state->pm_dev); ++#endif ++ } ++ ++ up(&state->sem); ++ return 0; ++} ++ ++ ++#ifdef CONFIG_PM ++ ++static int audio_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data) ++{ ++ audio_state_t *state = (audio_state_t *)pm_dev->data; ++ ++ switch (req) { ++ case PM_SUSPEND: /* enter D1-D3 */ ++ if (state->output_stream) ++ sa1100_dma_sleep(state->output_stream->dma_ch); ++ if (state->input_stream) ++ sa1100_dma_sleep(state->input_stream->dma_ch); ++ if (AUDIO_ACTIVE(state) && state->hw_shutdown) ++ state->hw_shutdown(state->data); ++ break; ++ case PM_RESUME: /* enter D0 */ ++ if (AUDIO_ACTIVE(state) && state->hw_init) ++ state->hw_init(state->data); ++ if (state->input_stream) ++ sa1100_dma_wakeup(state->input_stream->dma_ch); ++ if (state->output_stream) ++ sa1100_dma_wakeup(state->output_stream->dma_ch); ++ break; ++ } ++ return 0; ++} ++ ++#endif ++ ++ ++int sa1100_audio_attach(struct inode *inode, struct file *file, ++ audio_state_t *state) ++{ ++ int err, need_tx_dma; ++ ++ DPRINTK("audio_open\n"); ++ ++ down(&state->sem); ++ ++ /* access control */ ++ err = -ENODEV; ++ if ((file->f_mode & FMODE_WRITE) && !state->output_stream) ++ goto out; ++ if ((file->f_mode & FMODE_READ) && !state->input_stream) ++ goto out; ++ err = -EBUSY; ++ if ((file->f_mode & FMODE_WRITE) && state->wr_ref) ++ goto out; ++ if ((file->f_mode & FMODE_READ) && state->rd_ref) ++ goto out; ++ err = -EINVAL; ++ if ((file->f_mode & FMODE_READ) && state->need_tx_for_rx && !state->output_stream) ++ goto out; ++ ++ /* request DMA channels */ ++ if (state->skip_dma_init) ++ goto skip_dma; ++ need_tx_dma = ((file->f_mode & FMODE_WRITE) || ++ ((file->f_mode & FMODE_READ) && state->need_tx_for_rx)); ++ if (state->wr_ref || (state->rd_ref && state->need_tx_for_rx)) ++ need_tx_dma = 0; ++ if (need_tx_dma) { ++ err = sa1100_request_dma(&state->output_stream->dma_ch, ++ state->output_id, ++ state->output_dma); ++ if (err) ++ goto out; ++ } ++ if (file->f_mode & FMODE_READ) { ++ err = sa1100_request_dma(&state->input_stream->dma_ch, ++ state->input_id, ++ state->input_dma); ++ if (err) { ++ if (need_tx_dma) ++ sa1100_free_dma(state->output_stream->dma_ch); ++ goto out; ++ } ++ } ++skip_dma: ++ ++ /* now complete initialisation */ ++ if (!AUDIO_ACTIVE(state)) { ++ if (state->hw_init) ++ state->hw_init(state->data); ++#ifdef CONFIG_PM ++ state->pm_dev = pm_register(PM_SYS_DEV, 0, audio_pm_callback); ++ if (state->pm_dev) ++ state->pm_dev->data = state; ++#endif ++ } ++ ++ if ((file->f_mode & FMODE_WRITE)) { ++ state->wr_ref = 1; ++ audio_clear_buf(state->output_stream); ++ state->output_stream->fragsize = AUDIO_FRAGSIZE_DEFAULT; ++ state->output_stream->nbfrags = AUDIO_NBFRAGS_DEFAULT; ++ state->output_stream->mapped = 0; ++ sa1100_dma_set_callback(state->output_stream->dma_ch, ++ audio_dmaout_done_callback); ++ init_waitqueue_head(&state->output_stream->wq); ++ } ++ if (file->f_mode & FMODE_READ) { ++ state->rd_ref = 1; ++ audio_clear_buf(state->input_stream); ++ state->input_stream->fragsize = AUDIO_FRAGSIZE_DEFAULT; ++ state->input_stream->nbfrags = AUDIO_NBFRAGS_DEFAULT; ++ state->input_stream->mapped = 0; ++ sa1100_dma_set_callback(state->input_stream->dma_ch, ++ audio_dmain_done_callback); ++ init_waitqueue_head(&state->input_stream->wq); ++ } ++ ++ file->private_data = state; ++ file->f_op->release = audio_release; ++ file->f_op->write = audio_write; ++ file->f_op->read = audio_read; ++ file->f_op->mmap = audio_mmap; ++ file->f_op->poll = audio_poll; ++ file->f_op->ioctl = audio_ioctl; ++ file->f_op->llseek = audio_llseek; ++ err = 0; ++ ++out: ++ up(&state->sem); ++ return err; ++} ++ ++EXPORT_SYMBOL(sa1100_audio_attach); ++ ++MODULE_AUTHOR("Nicolas Pitre"); ++MODULE_DESCRIPTION("Common audio handling for the SA11x0 processor"); ++MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/sound/sa1100-audio.h linux-2.4.26-vrs1/drivers/sound/sa1100-audio.h +--- linux-2.4.26/drivers/sound/sa1100-audio.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/sound/sa1100-audio.h 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,68 @@ ++/* ++ * Common audio handling for the SA11x0 ++ * ++ * Copyright (c) 2000 Nicolas Pitre ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License. ++ */ ++ ++ ++/* ++ * Buffer Management ++ */ ++ ++typedef struct { ++ int size; /* buffer size */ ++ char *start; /* points to actual buffer */ ++ dma_addr_t dma_addr; /* physical buffer address */ ++ struct semaphore sem; /* down before touching the buffer */ ++ int master; /* owner for buffer allocation, contain size when true */ ++ struct audio_stream_s *stream; /* owning stream */ ++} audio_buf_t; ++ ++typedef struct audio_stream_s { ++ audio_buf_t *buffers; /* pointer to audio buffer structures */ ++ audio_buf_t *buf; /* current buffer used by read/write */ ++ u_int buf_idx; /* index for the pointer above... */ ++ u_int fragsize; /* fragment i.e. buffer size */ ++ u_int nbfrags; /* nbr of fragments i.e. buffers */ ++ int bytecount; /* nbr of processed bytes */ ++ int getptrCount; /* value of bytecount last time anyone asked via GETxPTR */ ++ int fragcount; /* nbr of fragment transitions */ ++ dmach_t dma_ch; /* DMA channel ID */ ++ wait_queue_head_t wq; /* for poll */ ++ int mapped:1; /* mmap()'ed buffers */ ++ int active:1; /* actually in progress */ ++ int stopped:1; /* might be active but stopped */ ++} audio_stream_t; ++ ++/* ++ * State structure for one instance ++ */ ++ ++typedef struct { ++ audio_stream_t *output_stream; ++ audio_stream_t *input_stream; ++ dma_device_t output_dma; ++ dma_device_t input_dma; ++ char *output_id; ++ char *input_id; ++ int rd_ref:1; /* open reference for recording */ ++ int wr_ref:1; /* open reference for playback */ ++ int need_tx_for_rx:1; /* if data must be sent while receiving */ ++ int tx_spinning:1; /* tx spinning active */ ++ int skip_dma_init:1; /* hack for the SA1111 */ ++ void *data; ++ void (*hw_init)(void *); ++ void (*hw_shutdown)(void *); ++ int (*client_ioctl)(struct inode *, struct file *, uint, ulong); ++ struct pm_dev *pm_dev; ++ struct semaphore sem; /* to protect against races in attach() */ ++} audio_state_t; ++ ++/* ++ * Functions exported by this module ++ */ ++extern int sa1100_audio_attach( struct inode *inode, struct file *file, ++ audio_state_t *state); +diff -urN linux-2.4.26/drivers/sound/sa1100ssp.c linux-2.4.26-vrs1/drivers/sound/sa1100ssp.c +--- linux-2.4.26/drivers/sound/sa1100ssp.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/sound/sa1100ssp.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,182 @@ ++/* ++ * Glue audio driver for a simple DAC on the SA1100's SSP port ++ * ++ * Copyright (c) 2001 Nicolas Pitre ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License. ++ * ++ * History: ++ * ++ * 2001-06-04 Nicolas Pitre Initial release. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "sa1100-audio.h" ++ ++ ++#undef DEBUG ++#ifdef DEBUG ++#define DPRINTK( x... ) printk( ##x ) ++#else ++#define DPRINTK( x... ) ++#endif ++ ++ ++#define AUDIO_NAME "SA1100 SSP audio" ++ ++#define AUDIO_FMT AFMT_S16_LE ++#define AUDIO_CHANNELS 2 ++ ++static int sample_rate = 44100; ++ ++ ++static void ssp_audio_init(void) ++{ ++ if (machine_is_lart()) { ++ unsigned long flags; ++ local_irq_save(flags); ++ ++ /* LART has the SSP port rewired to GPIO 10-13, 19 */ ++ /* alternate functions for the GPIOs */ ++ GAFR |= ( GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | ++ GPIO_SSP_SFRM | GPIO_SSP_CLK ); ++ ++ /* Set the direction: 10, 12, 13 output; 11, 19 input */ ++ GPDR |= ( GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM ); ++ GPDR &= ~( GPIO_SSP_RXD | GPIO_SSP_CLK ); ++ ++ /* enable SSP pin swap */ ++ PPAR |= PPAR_SPR; ++ ++ local_irq_restore(flags); ++ } ++ ++ /* turn on the SSP */ ++ Ser4SSCR0 = 0; ++ Ser4SSCR0 = (SSCR0_DataSize(16) | SSCR0_TI | SSCR0_SerClkDiv(2) | ++ SSCR0_SSE); ++ Ser4SSCR1 = (SSCR1_SClkIactL | SSCR1_SClk1P | SSCR1_ExtClk); ++} ++ ++static void ssp_audio_shutdown(void) ++{ ++ Ser4SSCR0 = 0; ++} ++ ++static int ssp_audio_ioctl( struct inode *inode, struct file *file, ++ uint cmd, ulong arg) ++{ ++ long val; ++ int ret = 0; ++ ++ /* ++ * These are platform dependent ioctls which are not handled by the ++ * generic sa1100-audio module. ++ */ ++ switch (cmd) { ++ case SNDCTL_DSP_STEREO: ++ ret = get_user(val, (int *) arg); ++ if (ret) ++ return ret; ++ /* Simple standard DACs are stereo only */ ++ ret = (val == 0) ? -EINVAL : 1; ++ return put_user(ret, (int *) arg); ++ ++ case SNDCTL_DSP_CHANNELS: ++ case SOUND_PCM_READ_CHANNELS: ++ /* Simple standard DACs are stereo only */ ++ return put_user(AUDIO_CHANNELS, (long *) arg); ++ ++ case SNDCTL_DSP_SPEED: ++ case SOUND_PCM_READ_RATE: ++ /* We assume the clock doesn't change */ ++ return put_user(sample_rate, (long *) arg); ++ ++ case SNDCTL_DSP_SETFMT: ++ case SNDCTL_DSP_GETFMTS: ++ /* Simple standard DACs are 16-bit only */ ++ return put_user(AUDIO_FMT, (long *) arg); ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static audio_stream_t output_stream; ++ ++static audio_state_t audio_state = { ++ output_stream: &output_stream, ++ output_dma: DMA_Ser4SSPWr, ++ output_id: "Generic SSP sound", ++ hw_init: ssp_audio_init, ++ hw_shutdown: ssp_audio_shutdown, ++ client_ioctl: ssp_audio_ioctl, ++ sem: __MUTEX_INITIALIZER(audio_state.sem), ++}; ++ ++static int ssp_audio_open(struct inode *inode, struct file *file) ++{ ++ return sa1100_audio_attach(inode, file, &audio_state); ++} ++ ++/* ++ * Missing fields of this structure will be patched with the call ++ * to sa1100_audio_attach(). ++ */ ++static struct file_operations ssp_audio_fops = { ++ open: ssp_audio_open, ++ owner: THIS_MODULE ++}; ++ ++static int audio_dev_id; ++ ++static int __init sa1100ssp_audio_init(void) ++{ ++ int ret; ++ ++ if (!machine_is_lart()) { ++ printk(KERN_ERR AUDIO_NAME ": no support for this SA-1100 design!\n"); ++ /* look at ssp_audio_init() for specific initialisations */ ++ return -ENODEV; ++ } ++ ++ /* register devices */ ++ audio_dev_id = register_sound_dsp(&ssp_audio_fops, -1); ++ ++ printk( KERN_INFO AUDIO_NAME " initialized\n" ); ++ return 0; ++} ++ ++static void __exit sa1100ssp_audio_exit(void) ++{ ++ unregister_sound_dsp(audio_dev_id); ++} ++ ++module_init(sa1100ssp_audio_init); ++module_exit(sa1100ssp_audio_exit); ++ ++MODULE_AUTHOR("Nicolas Pitre"); ++MODULE_DESCRIPTION("Glue audio driver for a simple DAC on the SA1100's SSP port"); ++ ++MODULE_PARM(sample_rate, "i"); ++MODULE_PARM_DESC(sample_rate, "Sample rate of the audio DAC, default is 44100"); ++ ++EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/sound/sa1111-ac97.c linux-2.4.26-vrs1/drivers/sound/sa1111-ac97.c +--- linux-2.4.26/drivers/sound/sa1111-ac97.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/sound/sa1111-ac97.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,518 @@ ++/* ++ * Glue audio driver for the CS4205 and CS4201 AC'97 codecs. ++ * largely based on the framework provided by sa1111-uda1341.c. ++ * ++ * Copyright (c) 2002 Bertrik Sikken (bertrik.sikken@technolution.nl) ++ * Copyright (c) 2002 Robert Whaley (rwhaley@applieddata.net) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License. ++ * ++ * This driver makes use of the ac97_codec module (for mixer registers) ++ * and the sa1100-audio module (for DMA). ++ * ++ * History: ++ * ++ * 2002-04-04 Initial version. ++ * 2002-04-10 Updated mtd_audio_init to improve choppy sound ++ * and hanging sound issue. ++ * 2002-05-16 Updated for ADS Bitsy+ Robert Whaley ++ * 2002-06-28 Cleanup and added retry for read register timeouts ++ * 2002-08-14 Updated for ADS AGC Robert Whaley ++ * 2002-12-26 Cleanup, remove CONFIG_PM (it's handled by sa1100-audio.c) ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "sa1100-audio.h" ++ ++/* SAC FIFO depth, low nibble is transmit fifo, high nibble is receive FIFO */ ++#define SAC_FIFO_DEPTH 0x77 ++ ++// #define DEBUG ++ ++#ifdef DEBUG ++#define DPRINTK( x... ) printk( ##x ) ++#else ++#define DPRINTK( x... ) ++#endif ++ ++/* ++ Our codec data ++*/ ++static struct ac97_codec ac97codec; ++static int audio_dev_id, mixer_dev_id; ++static audio_stream_t output_stream, input_stream; ++ ++/* proc info */ ++ ++struct proc_dir_entry *ac97_ps; ++ ++static int sa1111_ac97_set_adc_rate(long rate); ++static void sa1111_ac97_write_reg(struct ac97_codec *dev, u8 reg, u16 val); ++static u16 sa1111_ac97_read_reg(struct ac97_codec *dev, u8 reg); ++ ++static int ++mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) ++{ ++ /* ++ * We only accept mixer (type 'M') ioctls. ++ */ ++ if (_IOC_TYPE(cmd) != 'M') { ++ return -EINVAL; ++ } ++ ++ /* pass the ioctl to the ac97 mixer */ ++ return ac97codec.mixer_ioctl(&ac97codec, cmd, arg); ++} ++ ++ ++static struct file_operations sa1111_ac97_mixer_fops = { ++ ioctl: mixer_ioctl, ++ owner: THIS_MODULE ++}; ++ ++static void sa1111_ac97_power_off(void *dummy) ++{ ++#ifdef CONFIG_SA1100_ADSBITSYPLUS ++ /* turn off audio and audio amp */ ++ ADS_CPLD_PCON |= (ADS_PCON_AUDIO_ON | ADS_PCON_AUDIOPA_ON); ++ ++ /* make GPIO11 high impeadence */ ++ GPDR &= ~GPIO_GPIO11; ++ ++ /* disable SACR0 so we can make these pins high impeadence */ ++ SACR0 &= ~SACR0_ENB; ++ ++ /* make BIT_CLK, SDATA_OUT, and SYNC high impeadence */ ++ PC_DDR |= (GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); ++ ++#endif ++ ++#ifdef CONFIG_SA1100_ADSAGC ++ /* turn off audio and audio amp */ ++ ADS_CR1 &= ~(ADS_CR1_CODEC | ADS_CR1_AMP); ++ ++ /* disable SACR0 so we can make these pins high impeadence */ ++ SACR0 &= ~SACR0_ENB; ++ ++ /* make BIT_CLK, SDATA_OUT, and SYNC high impeadence */ ++ PC_DDR |= (GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3); ++ ++#endif ++} ++ ++ ++static void sa1111_ac97_power_on(void *dummy) ++{ ++ int ret, i; ++ ++ /* disable L3 */ ++ SACR1 = 0; ++ ++ SKPCR |= (SKPCR_ACCLKEN); /* enable ac97 clock */ ++ udelay(50); ++ ++ /* BIT_CLK is input to SA1111, DMA thresholds 9 (both dirs) */ ++ SACR0 |= SACR0_BCKD | (SAC_FIFO_DEPTH << 8); ++ ++ /* reset SAC registers */ ++ SACR0 &= ~SACR0_RST; ++ udelay(50); ++ SACR0 |= SACR0_RST; ++ udelay(50); ++ SACR0 &= ~SACR0_RST; ++ ++ /* setup SA1111 to use AC'97 */ ++ SBI_SKCR |= SKCR_SELAC; /* select ac97 */ ++ udelay(50); ++ ++ /* issue a cold AC97 reset */ ++#ifdef CONFIG_SA1100_ADSBITSYPLUS ++ ++ /* initialize reset line */ ++ GAFR &= ~GPIO_GPIO11; ++ GPDR |= GPIO_GPIO11; ++ GPSR = GPIO_GPIO11; ++ ++ /* turn on audio and audio amp */ ++ ADS_CPLD_PCON &= ~(ADS_PCON_AUDIO_ON | ADS_PCON_AUDIOPA_ON); ++ mdelay(5); ++ ++ /* reset by lowering the reset pin momentarily */ ++ DPRINTK("reseting codec via GPIO11\n"); ++ GPCR = GPIO_GPIO11; ++ udelay(5); ++ GPSR = GPIO_GPIO11; ++ udelay(10); ++ ++#endif ++#ifdef CONFIG_SA1100_ADSAGC ++ ++ /* turn on audio and audio amp */ ++ DPRINTK("before turning on power. ADS_CR1: %x\n", ADS_CR1); ++ ADS_CR1 |= (ADS_CR1_AMP | ADS_CR1_CODEC); ++ DPRINTK("after turnning on power. ADS_CR1: %x\n", ADS_CR1); ++ mdelay(5); ++ ++ /* reset by lowering the reset pin momentarily */ ++ DPRINTK("reseting codec via CPLD\n"); ++ ADS_CR1 |= ADS_CR1_AUDIO_RST; ++ DPRINTK("after reset1. ADS_CR1: %x\n", ADS_CR1); ++ udelay(5); ++ ADS_CR1 &= ~ADS_CR1_AUDIO_RST; ++ DPRINTK("after reset2. ADS_CR1: %x\n", ADS_CR1); ++ udelay(10); ++ ++#endif ++ SACR2 = 0; ++ udelay(50); ++ ++ DPRINTK("before SW reset: SACR2: %x\n", SACR2); ++ SACR2 = SACR2_RESET; ++ DPRINTK("after SW reset: SACR2: %x\n", SACR2); ++ udelay(50); ++ ++ /* set AC97 slot 3 and 4 (PCM out) to valid */ ++ SACR2 = (SACR2_RESET | SACR2_TS3V | SACR2_TS4V); ++ ++ /* enable SAC */ ++ SACR0 |= SACR0_ENB; ++ ++ i = 100; ++ while (!(SASR1 & SASR1_CRDY)) { ++ if (!i--) { ++ printk("Didn't get CRDY. SASR1=%x SKID=%x\n", SASR1, SBI_SKID); ++ break; ++ } ++ udelay(50); ++ } ++ ++ if (!(ret = ac97_probe_codec(&ac97codec))) { ++ printk("ac97_probe_codec failed (%d)\n", ret); ++ return; ++ } ++ ++ /* mic ADC on, disable VRA, disable VRM */ ++ sa1111_ac97_write_reg(&ac97codec, AC97_EXTENDED_STATUS, 0x0200); ++} ++ ++ ++/* ++ * Audio interface ++ */ ++ ++ ++static int sa1111_ac97_audio_ioctl(struct inode *inode, struct file *file, ++ uint cmd, ulong arg) ++{ ++ long val; ++ int ret = 0; ++ ++ DPRINTK("sa1111_ac97_audio_ioctl\n"); ++ ++ /* ++ * These are platform dependent ioctls which are not handled by the ++ * generic sa1100-audio module. ++ */ ++ switch (cmd) { ++ case SNDCTL_DSP_STEREO: ++ ret = get_user(val, (int *) arg); ++ if (ret) { ++ return ret; ++ } ++ /* the cs42xx is stereo only */ ++ ret = (val == 0) ? -EINVAL : 1; ++ return put_user(ret, (int *) arg); ++ ++ case SNDCTL_DSP_CHANNELS: ++ case SOUND_PCM_READ_CHANNELS: ++ /* the cs42xx is stereo only */ ++ return put_user(2, (long *) arg); ++ ++#define SA1100_AC97_IOCTL_EXTRAS ++ ++#ifdef SA1100_AC97_IOCTL_EXTRAS ++ ++#define SNDCTL_DSP_AC97_CMD _SIOWR('P', 99, int) ++#define SNDCTL_DSP_INPUT_SPEED _SIOWR('P', 98, int) ++#define SOUND_PCM_READ_INPUT_RATE _SIOWR('P', 97, int) ++ ++ case SNDCTL_DSP_AC97_CMD: ++ ++ ret = get_user(val, (long *) arg); ++ if (ret) { ++ break; ++ } ++ sa1111_ac97_write_reg(&ac97codec, (u8) ((val & 0xff000000) >> 24), (u16) (val & 0xffff)); ++ return 0; ++ ++ ++ case SNDCTL_DSP_INPUT_SPEED: ++ ret = get_user(val, (long *) arg); ++ // acc code here to set the speed ++ if (ret) { ++ break; ++ } ++ // note that this only changes the ADC rate, not the ++ // rate of the DAC. ++ ret = sa1111_ac97_set_adc_rate(val); ++ if (ret) ++ break; ++ return put_user(val, (long *) arg); ++ ++ case SOUND_PCM_READ_INPUT_RATE: ++ ++ return put_user((long) sa1111_ac97_read_reg(&ac97codec, 0x32), (long *) arg); ++ ++ ++#endif ++ ++ case SNDCTL_DSP_SPEED: ++ ret = get_user(val, (long *) arg); ++ if (ret) { ++ break; ++ } ++ ++ case SOUND_PCM_READ_RATE: ++ /* only 48 kHz playback is supported by the SA1111 */ ++ return put_user(48000L, (long *) arg); ++ ++ case SNDCTL_DSP_SETFMT: ++ case SNDCTL_DSP_GETFMTS: ++ /* we can do 16-bit only */ ++ return put_user(AFMT_S16_LE, (long *) arg); ++ ++ default: ++ /* Maybe this is meant for the mixer (As per OSS Docs) */ ++ return mixer_ioctl(inode, file, cmd, arg); ++ } ++ ++ return ret; ++} ++ ++ ++static audio_state_t audio_state = { ++ output_stream: &output_stream, ++ input_stream: &input_stream, ++ skip_dma_init: 1, /* done locally */ ++ hw_init: sa1111_ac97_power_on, ++ hw_shutdown: sa1111_ac97_power_off, ++ client_ioctl: sa1111_ac97_audio_ioctl, ++ sem: __MUTEX_INITIALIZER(audio_state.sem), ++}; ++ ++ ++static int sa1111_ac97_audio_open(struct inode *inode, struct file *file) ++{ ++ return sa1100_audio_attach(inode, file, &audio_state); ++} ++ ++ ++/* ++ * Missing fields of this structure will be patched with the call ++ * to sa1100_audio_attach(). ++ */ ++static struct file_operations sa1111_ac97_audio_fops = { ++ open: sa1111_ac97_audio_open, ++ owner: THIS_MODULE ++}; ++ ++ ++static void sa1111_ac97_write_reg(struct ac97_codec *dev, u8 reg, u16 val) ++{ ++ int i; ++ ++ /* reset status bits */ ++ SASCR = SASCR_DTS; ++ ++ /* write command and data registers */ ++ ACCAR = reg << 12; ++ ACCDR = val << 4; ++ ++ /* wait for data to be transmitted */ ++ i = 0; ++ while ((SASR1 & SASR1_CADT) == 0) { ++ udelay(50); ++ if (++i > 10) { ++ DPRINTK("sa1111_ac97_write_reg failed (data not transmitted. SASR1: %x)\n", SASR1); ++ break; ++ } ++ } ++ ++ DPRINTK("<%03d> sa1111_ac97_write_reg, [%02X]=%04X\n", i, reg, val); ++} ++ ++ ++static u16 sa1111_ac97_read_reg(struct ac97_codec *dev, u8 reg) ++{ ++ u16 val; ++ int i; ++ int retry = 10; ++ ++ do { ++ /* reset status bits */ ++ SASCR = SASCR_RDD | SASCR_STO; ++ ++ /* write command register */ ++ ACCAR = (reg | 0x80) << 12; ++ ACCDR = 0; ++ ++ /* wait for SADR bit in SASR1 */ ++ i = 0; ++ while ((SASR1 & SASR1_SADR) == 0) { ++ udelay(50); ++ if (++i > 10) { ++ DPRINTK("<---> sa1111_ac97_read_reg failed\n"); ++ retry--; ++ break; ++ } ++ if ((SASR1 & SASR1_RSTO) != 0) { ++ DPRINTK("sa1111_ac97_read_reg *timeout*\n"); ++ retry--; ++ break; ++ } ++ } ++ ++ } while ((SASR1 & SASR1_SADR) == 0 && retry > 0); ++ ++ val = ACSDR >> 4; ++ ++ DPRINTK("<%03d> sa1111_ac97_read_reg, [%02X]=%04X\n", i, reg, val); ++ return val; ++} ++ ++ ++/* wait for codec ready */ ++static void sa1111_ac97_ready(struct ac97_codec *dev) ++{ ++ int i; ++ u16 val; ++ ++ i = 0; ++ while ((SASR1 & SASR1_CRDY) == 0) { ++ udelay(50); ++ if (++i > 10) { ++ DPRINTK("sa1111_ac97_ready failed\n"); ++ return; ++ } ++ } ++ DPRINTK("codec_ready bit took %d cycles\n", i); ++ ++ /* Wait for analog parts of codec to initialise */ ++ i = 0; ++ do { ++ val = sa1111_ac97_read_reg(&ac97codec, AC97_POWER_CONTROL); ++ if (++i > 100) { ++ break; ++ } ++ mdelay(10); ++ } while ((val & 0xF) != 0xF || val == 0xFFFF); ++ ++ /* the cs42xx typically takes 150 ms to initialise */ ++ ++ DPRINTK("analog init took %d cycles\n", i); ++} ++ ++ ++static int __init sa1111_ac97_init(void) ++{ ++ int ret; ++ ++ // SBI_SKCR |= SKCR_RCLKEN; ++ ++ DPRINTK("sa1111_ac97_init\n"); ++ ++ /* install the ac97 mixer module */ ++ ac97codec.codec_read = sa1111_ac97_read_reg; ++ ac97codec.codec_write = sa1111_ac97_write_reg; ++ ac97codec.codec_wait = sa1111_ac97_ready; ++ ++ /* Acquire and initialize DMA */ ++ ret = sa1111_sac_request_dma(&output_stream.dma_ch, "SA1111 audio out", ++ SA1111_SAC_XMT_CHANNEL); ++ if (ret < 0) { ++ printk("DMA request for SAC output failed\n"); ++ return ret; ++ } ++ ++ ret = sa1111_sac_request_dma(&input_stream.dma_ch, "SA1111 audio in", ++ SA1111_SAC_RCV_CHANNEL); ++ if (ret < 0) { ++ printk("DMA request for SAC input failed\n"); ++ sa1100_free_dma(output_stream.dma_ch); ++ return ret; ++ } ++ /* register devices */ ++ audio_dev_id = register_sound_dsp(&sa1111_ac97_audio_fops, -1); ++ mixer_dev_id = register_sound_mixer(&sa1111_ac97_mixer_fops, -1); ++ ++ ++ /* setup proc entry */ ++ ac97_ps = create_proc_read_entry ("driver/sa1111-ac97", 0, NULL, ++ ac97_read_proc, &ac97codec); ++ ++ return 0; ++} ++ ++ ++static void __exit sa1111_ac97_exit(void) ++{ ++ SKPCR &= ~SKPCR_ACCLKEN; /* disable ac97 clock */ ++ SBI_SKCR &= ~SKCR_SELAC; /* deselect ac97 */ ++ ++ unregister_sound_dsp(audio_dev_id); ++ unregister_sound_mixer(mixer_dev_id); ++ sa1100_free_dma(output_stream.dma_ch); ++ sa1100_free_dma(input_stream.dma_ch); ++} ++ ++static int sa1111_ac97_set_adc_rate(long rate) ++{ ++ ++ // note this only changes the rate of the ADC, the DAC is fixed at 48K. ++ // this is due to limitations of the SA1111 chip ++ ++ u16 code = rate; ++ ++ switch (rate) { ++ case 8000: ++ case 11025: ++ case 16000: ++ case 22050: ++ case 32000: ++ case 44100: ++ case 48000: ++ break; ++ default: ++ return -1; ++ } ++ sa1111_ac97_write_reg(&ac97codec, 0x2A, 0x0001); ++ sa1111_ac97_write_reg(&ac97codec, 0x32, code); ++ return 0; ++} ++ ++module_init(sa1111_ac97_init); ++module_exit(sa1111_ac97_exit); ++ ++MODULE_AUTHOR("Bertrik Sikken, Technolution B.V., Netherlands"); ++MODULE_DESCRIPTION("Glue audio driver for AC'97 codec"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/sound/sa1111-uda1341.c linux-2.4.26-vrs1/drivers/sound/sa1111-uda1341.c +--- linux-2.4.26/drivers/sound/sa1111-uda1341.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/sound/sa1111-uda1341.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,282 @@ ++/* ++ * Glue audio driver for the SA1111 compagnon chip & Philips UDA1341 codec. ++ * ++ * Copyright (c) 2000 John Dorsey ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License. ++ * ++ * History: ++ * ++ * 2000-09-04 John Dorsey SA-1111 Serial Audio Controller support ++ * was initially added to the sa1100-uda1341.c ++ * driver. ++ * ++ * 2001-06-03 Nicolas Pitre Made this file a separate module, based on ++ * the former sa1100-uda1341.c driver. ++ * ++ * 2001-09-23 Russell King Remove old L3 bus driver. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sa1100-audio.h" ++ ++#undef DEBUG ++#ifdef DEBUG ++#define DPRINTK( x... ) printk( ##x ) ++#else ++#define DPRINTK( x... ) ++#endif ++ ++ ++/* ++ * Definitions ++ */ ++ ++#define AUDIO_RATE_DEFAULT 22050 ++ ++#define AUDIO_CLK_BASE 561600 ++ ++ ++ ++/* ++ * Mixer interface ++ */ ++ ++static struct l3_client uda1341; ++ ++static int ++mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) ++{ ++ /* ++ * We only accept mixer (type 'M') ioctls. ++ */ ++ if (_IOC_TYPE(cmd) != 'M') ++ return -EINVAL; ++ ++ return l3_command(&uda1341, cmd, (void *)arg); ++} ++ ++static struct file_operations uda1341_mixer_fops = { ++ ioctl: mixer_ioctl, ++ owner: THIS_MODULE ++}; ++ ++ ++/* ++ * Audio interface ++ */ ++ ++static int audio_clk_div = (AUDIO_CLK_BASE + AUDIO_RATE_DEFAULT/2)/AUDIO_RATE_DEFAULT; ++ ++static void sa1111_audio_init(void *dummy) ++{ ++#ifdef CONFIG_ASSABET_NEPONSET ++ if (machine_is_assabet()) { ++ /* Select I2S audio (instead of AC-Link) */ ++ AUD_CTL = AUD_SEL_1341; ++ } ++#endif ++#ifdef CONFIG_SA1100_JORNADA720 ++ if (machine_is_jornada720()) { ++ /* LDD4 is speaker, LDD3 is microphone */ ++ PPSR &= ~(PPC_LDD3 | PPC_LDD4); ++ PPDR |= PPC_LDD3 | PPC_LDD4; ++ PPSR |= PPC_LDD4; /* enable speaker */ ++ PPSR |= PPC_LDD3; /* enable microphone */ ++ } ++#endif ++ ++ SBI_SKCR &= ~SKCR_SELAC; ++ ++ /* Enable the I2S clock and L3 bus clock: */ ++ SKPCR |= (SKPCR_I2SCLKEN | SKPCR_L3CLKEN); ++ ++ /* Activate and reset the Serial Audio Controller */ ++ SACR0 |= (SACR0_ENB | SACR0_RST); ++ mdelay(5); ++ SACR0 &= ~SACR0_RST; ++ ++ /* For I2S, BIT_CLK is supplied internally. The "SA-1111 ++ * Specification Update" mentions that the BCKD bit should ++ * be interpreted as "0 = output". Default clock divider ++ * is 22.05kHz. ++ * ++ * Select I2S, L3 bus. "Recording" and "Replaying" ++ * (receive and transmit) are enabled. ++ */ ++ SACR1 = SACR1_L3EN; ++ SKAUD = audio_clk_div - 1; ++ ++ /* Initialize the UDA1341 internal state */ ++ l3_open(&uda1341); ++} ++ ++static void sa1111_audio_shutdown(void *dummy) ++{ ++ l3_close(&uda1341); ++ SACR0 &= ~SACR0_ENB; ++} ++ ++static int sa1111_audio_ioctl( struct inode *inode, struct file *file, ++ uint cmd, ulong arg) ++{ ++ long val; ++ int ret = 0; ++ ++ switch (cmd) { ++ case SNDCTL_DSP_STEREO: ++ ret = get_user(val, (int *) arg); ++ if (ret) ++ return ret; ++ /* the UDA1341 is stereo only */ ++ ret = (val == 0) ? -EINVAL : 1; ++ return put_user(ret, (int *) arg); ++ ++ case SNDCTL_DSP_CHANNELS: ++ case SOUND_PCM_READ_CHANNELS: ++ /* the UDA1341 is stereo only */ ++ return put_user(2, (long *) arg); ++ ++ case SNDCTL_DSP_SPEED: ++ ret = get_user(val, (long *) arg); ++ if (ret) break; ++ if (val < 8000) val = 8000; ++ if (val > 48000) val = 48000; ++ audio_clk_div = (AUDIO_CLK_BASE + val/2)/val; ++ SKAUD = audio_clk_div - 1; ++ /* fall through */ ++ ++ case SOUND_PCM_READ_RATE: ++ return put_user(AUDIO_CLK_BASE/audio_clk_div, (long *) arg); ++ ++ case SNDCTL_DSP_SETFMT: ++ case SNDCTL_DSP_GETFMTS: ++ /* we can do 16-bit only */ ++ return put_user(AFMT_S16_LE, (long *) arg); ++ ++ default: ++ /* Maybe this is meant for the mixer (as per OSS Docs) */ ++ return mixer_ioctl(inode, file, cmd, arg); ++ } ++ ++ return ret; ++} ++ ++static audio_stream_t output_stream, input_stream; ++ ++static audio_state_t audio_state = { ++ output_stream: &output_stream, ++ input_stream: &input_stream, ++ skip_dma_init: 1, /* done locally */ ++ hw_init: sa1111_audio_init, ++ hw_shutdown: sa1111_audio_shutdown, ++ client_ioctl: sa1111_audio_ioctl, ++ sem: __MUTEX_INITIALIZER(audio_state.sem), ++}; ++ ++static int sa1111_audio_open(struct inode *inode, struct file *file) ++{ ++ return sa1100_audio_attach(inode, file, &audio_state); ++} ++ ++/* ++ * Missing fields of this structure will be patched with the call ++ * to sa1100_audio_attach(). ++ */ ++static struct file_operations sa1111_audio_fops = { ++ open: sa1111_audio_open, ++ owner: THIS_MODULE ++}; ++ ++static int audio_dev_id, mixer_dev_id; ++ ++static int __init sa1111_uda1341_init(void) ++{ ++ struct uda1341_cfg cfg; ++ int ret; ++ ++ if ( !( (machine_is_assabet() && machine_has_neponset()) || ++ machine_is_jornada720() || ++ machine_is_badge4() )) ++ return -ENODEV; ++ ++ if (!request_mem_region(_SACR0, 512, "sound")) ++ return -EBUSY; ++ ++ ret = l3_attach_client(&uda1341, "l3-sa1111", "uda1341"); ++ if (ret) ++ goto out; ++ ++ /* Acquire and initialize DMA */ ++ ret = sa1111_sac_request_dma(&output_stream.dma_ch, "SA1111 audio out", ++ SA1111_SAC_XMT_CHANNEL); ++ if (ret < 0) ++ goto release_l3; ++ ++ ret = sa1111_sac_request_dma(&input_stream.dma_ch, "SA1111 audio in", ++ SA1111_SAC_RCV_CHANNEL); ++ if (ret < 0) ++ goto release_dma; ++ ++ cfg.fs = 256; ++ cfg.format = FMT_I2S; ++ l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); ++ ++ /* register devices */ ++ audio_dev_id = register_sound_dsp(&sa1111_audio_fops, -1); ++ mixer_dev_id = register_sound_mixer(&uda1341_mixer_fops, -1); ++ ++ printk(KERN_INFO "Sound: SA1111 UDA1341: dsp id %d mixer id %d\n", ++ audio_dev_id, mixer_dev_id); ++ return 0; ++ ++release_dma: ++ sa1100_free_dma(output_stream.dma_ch); ++release_l3: ++ l3_detach_client(&uda1341); ++out: ++ release_mem_region(_SACR0, 512); ++ return ret; ++} ++ ++static void __exit sa1111_uda1341_exit(void) ++{ ++ unregister_sound_dsp(audio_dev_id); ++ unregister_sound_mixer(mixer_dev_id); ++ sa1100_free_dma(output_stream.dma_ch); ++ sa1100_free_dma(input_stream.dma_ch); ++ l3_detach_client(&uda1341); ++ ++ release_mem_region(_SACR0, 512); ++} ++ ++module_init(sa1111_uda1341_init); ++module_exit(sa1111_uda1341_exit); ++ ++MODULE_AUTHOR("John Dorsey, Nicolas Pitre"); ++MODULE_DESCRIPTION("Glue audio driver for the SA1111 compagnon chip & Philips UDA1341 codec."); ++ ++EXPORT_NO_SYMBOLS; +diff -urN linux-2.4.26/drivers/sound/uda1341.c linux-2.4.26-vrs1/drivers/sound/uda1341.c +--- linux-2.4.26/drivers/sound/uda1341.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/sound/uda1341.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,511 @@ ++/* ++ * Philips UDA1341 mixer device driver ++ * ++ * Copyright (c) 2000 Nicolas Pitre ++ * ++ * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License. ++ * ++ * History: ++ * ++ * 2000-05-21 Nicolas Pitre Initial release. ++ * ++ * 2000-08-19 Erik Bunce More inline w/ OSS API and UDA1341 docs ++ * including fixed AGC and audio source handling ++ * ++ * 2000-11-30 Nicolas Pitre - More mixer functionalities. ++ * ++ * 2001-06-03 Nicolas Pitre Made this file a separate module, based on ++ * the former sa1100-uda1341.c driver. ++ * ++ * 2001-08-13 Russell King Re-written as part of the L3 interface ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define DEF_VOLUME 65 ++ ++/* ++ * UDA1341 L3 address and command types ++ */ ++#define UDA1341_L3ADDR 5 ++#define UDA1341_DATA0 (UDA1341_L3ADDR << 2 | 0) ++#define UDA1341_DATA1 (UDA1341_L3ADDR << 2 | 1) ++#define UDA1341_STATUS (UDA1341_L3ADDR << 2 | 2) ++ ++struct uda1341_regs { ++ unsigned char stat0; ++#define STAT0 0x00 ++#define STAT0_RST (1 << 6) ++#define STAT0_SC_MASK (3 << 4) ++#define STAT0_SC_512FS (0 << 4) ++#define STAT0_SC_384FS (1 << 4) ++#define STAT0_SC_256FS (2 << 4) ++#define STAT0_IF_MASK (7 << 1) ++#define STAT0_IF_I2S (0 << 1) ++#define STAT0_IF_LSB16 (1 << 1) ++#define STAT0_IF_LSB18 (2 << 1) ++#define STAT0_IF_LSB20 (3 << 1) ++#define STAT0_IF_MSB (4 << 1) ++#define STAT0_IF_LSB16MSB (5 << 1) ++#define STAT0_IF_LSB18MSB (6 << 1) ++#define STAT0_IF_LSB20MSB (7 << 1) ++#define STAT0_DC_FILTER (1 << 0) ++ ++ unsigned char stat1; ++#define STAT1 0x80 ++#define STAT1_DAC_GAIN (1 << 6) /* gain of DAC */ ++#define STAT1_ADC_GAIN (1 << 5) /* gain of ADC */ ++#define STAT1_ADC_POL (1 << 4) /* polarity of ADC */ ++#define STAT1_DAC_POL (1 << 3) /* polarity of DAC */ ++#define STAT1_DBL_SPD (1 << 2) /* double speed playback */ ++#define STAT1_ADC_ON (1 << 1) /* ADC powered */ ++#define STAT1_DAC_ON (1 << 0) /* DAC powered */ ++ ++ unsigned char data0_0; ++#define DATA0 0x00 ++#define DATA0_VOLUME_MASK 0x3f ++#define DATA0_VOLUME(x) (x) ++ ++ unsigned char data0_1; ++#define DATA1 0x40 ++#define DATA1_BASS(x) ((x) << 2) ++#define DATA1_BASS_MASK (15 << 2) ++#define DATA1_TREBLE(x) ((x)) ++#define DATA1_TREBLE_MASK (3) ++ ++ unsigned char data0_2; ++#define DATA2 0x80 ++#define DATA2_PEAKAFTER (1 << 5) ++#define DATA2_DEEMP_NONE (0 << 3) ++#define DATA2_DEEMP_32KHz (1 << 3) ++#define DATA2_DEEMP_44KHz (2 << 3) ++#define DATA2_DEEMP_48KHz (3 << 3) ++#define DATA2_MUTE (1 << 2) ++#define DATA2_FILTER_FLAT (0 << 0) ++#define DATA2_FILTER_MIN (1 << 0) ++#define DATA2_FILTER_MAX (3 << 0) ++ ++#define EXTADDR(n) (0xc0 | (n)) ++#define EXTDATA(d) (0xe0 | (d)) ++ ++ unsigned char ext0; ++#define EXT0 0 ++#define EXT0_CH1_GAIN(x) (x) ++ ++ unsigned char ext1; ++#define EXT1 1 ++#define EXT1_CH2_GAIN(x) (x) ++ ++ unsigned char ext2; ++#define EXT2 2 ++#define EXT2_MIC_GAIN_MASK (7 << 2) ++#define EXT2_MIC_GAIN(x) ((x) << 2) ++#define EXT2_MIXMODE_DOUBLEDIFF (0) ++#define EXT2_MIXMODE_CH1 (1) ++#define EXT2_MIXMODE_CH2 (2) ++#define EXT2_MIXMODE_MIX (3) ++ ++ unsigned char ext4; ++#define EXT4 4 ++#define EXT4_AGC_ENABLE (1 << 4) ++#define EXT4_INPUT_GAIN_MASK (3) ++#define EXT4_INPUT_GAIN(x) ((x) & 3) ++ ++ unsigned char ext5; ++#define EXT5 5 ++#define EXT5_INPUT_GAIN(x) ((x) >> 2) ++ ++ unsigned char ext6; ++#define EXT6 6 ++#define EXT6_AGC_CONSTANT_MASK (7 << 2) ++#define EXT6_AGC_CONSTANT(x) ((x) << 2) ++#define EXT6_AGC_LEVEL_MASK (3) ++#define EXT6_AGC_LEVEL(x) (x) ++}; ++ ++#define REC_MASK (SOUND_MASK_LINE | SOUND_MASK_MIC) ++#define DEV_MASK (REC_MASK | SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE) ++ ++struct uda1341 { ++ struct uda1341_regs regs; ++ int active; ++ unsigned short volume; ++ unsigned short bass; ++ unsigned short treble; ++ unsigned short line; ++ unsigned short mic; ++ int mod_cnt; ++}; ++ ++#define ADD_FIELD(reg,field) \ ++ *p++ = reg | uda->regs.field ++ ++#define ADD_EXTFIELD(reg,field) \ ++ *p++ = EXTADDR(reg); \ ++ *p++ = EXTDATA(uda->regs.field); ++ ++static void uda1341_sync(struct l3_client *clnt) ++{ ++ struct uda1341 *uda = clnt->driver_data; ++ char buf[24], *p = buf; ++ ++ ADD_FIELD(STAT0, stat0); ++ ADD_FIELD(STAT1, stat1); ++ ++ if (p != buf) ++ l3_write(clnt, UDA1341_STATUS, buf, p - buf); ++ ++ p = buf; ++ ADD_FIELD(DATA0, data0_0); ++ ADD_FIELD(DATA1, data0_1); ++ ADD_FIELD(DATA2, data0_2); ++ ADD_EXTFIELD(EXT0, ext0); ++ ADD_EXTFIELD(EXT1, ext1); ++ ADD_EXTFIELD(EXT2, ext2); ++ ADD_EXTFIELD(EXT4, ext4); ++ ADD_EXTFIELD(EXT5, ext5); ++ ADD_EXTFIELD(EXT6, ext6); ++ ++ if (p != buf) ++ l3_write(clnt, UDA1341_DATA0, buf, p - buf); ++} ++ ++static void uda1341_cmd_init(struct l3_client *clnt) ++{ ++ struct uda1341 *uda = clnt->driver_data; ++ char buf[2]; ++ ++ uda->active = 1; ++ ++ buf[0] = uda->regs.stat0 | STAT0_RST; ++ buf[1] = uda->regs.stat0; ++ ++ l3_write(clnt, UDA1341_STATUS, buf, 2); ++ ++ /* resend all parameters */ ++ uda1341_sync(clnt); ++} ++ ++static int uda1341_configure(struct l3_client *clnt, struct uda1341_cfg *conf) ++{ ++ struct uda1341 *uda = clnt->driver_data; ++ int ret = 0; ++ ++ uda->regs.stat0 &= ~(STAT0_SC_MASK | STAT0_IF_MASK); ++ ++ switch (conf->fs) { ++ case 512: uda->regs.stat0 |= STAT0_SC_512FS; break; ++ case 384: uda->regs.stat0 |= STAT0_SC_384FS; break; ++ case 256: uda->regs.stat0 |= STAT0_SC_256FS; break; ++ default: ret = -EINVAL; break; ++ } ++ ++ switch (conf->format) { ++ case FMT_I2S: uda->regs.stat0 |= STAT0_IF_I2S; break; ++ case FMT_LSB16: uda->regs.stat0 |= STAT0_IF_LSB16; break; ++ case FMT_LSB18: uda->regs.stat0 |= STAT0_IF_LSB18; break; ++ case FMT_LSB20: uda->regs.stat0 |= STAT0_IF_LSB20; break; ++ case FMT_MSB: uda->regs.stat0 |= STAT0_IF_MSB; break; ++ case FMT_LSB16MSB: uda->regs.stat0 |= STAT0_IF_LSB16MSB; break; ++ case FMT_LSB18MSB: uda->regs.stat0 |= STAT0_IF_LSB18MSB; break; ++ case FMT_LSB20MSB: uda->regs.stat0 |= STAT0_IF_LSB20MSB; break; ++ } ++ ++ if (ret == 0 && uda->active) { ++ char buf = uda->regs.stat0 | STAT0; ++ l3_write(clnt, UDA1341_STATUS, &buf, 1); ++ } ++ return ret; ++} ++ ++static int uda1341_update_direct(struct l3_client *clnt, int cmd, void *arg) ++{ ++ struct uda1341 *uda = clnt->driver_data; ++ struct l3_gain *v = arg; ++ char newreg; ++ int val; ++ ++ switch (cmd) { ++ case L3_SET_VOLUME: /* set volume. val = 0 to 100 => 62 to 1 */ ++ uda->regs.data0_0 = DATA0_VOLUME(62 - ((v->left * 61) / 100)); ++ newreg = uda->regs.data0_0 | DATA0; ++ break; ++ ++ case L3_SET_BASS: /* set bass. val = 50 to 100 => 0 to 12 */ ++ val = v->left - 50; ++ if (val < 0) ++ val = 0; ++ uda->regs.data0_1 &= ~DATA1_BASS_MASK; ++ uda->regs.data0_1 |= DATA1_BASS((val * 12) / 50); ++ newreg = uda->regs.data0_1 | DATA1; ++ break; ++ ++ case L3_SET_TREBLE: /* set treble. val = 50 to 100 => 0 to 3 */ ++ val = v->left - 50; ++ if (val < 0) ++ val = 0; ++ uda->regs.data0_1 &= ~DATA1_TREBLE_MASK; ++ uda->regs.data0_1 |= DATA1_TREBLE((val * 3) / 50); ++ newreg = uda->regs.data0_1 | DATA1; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ if (uda->active) ++ l3_write(clnt, UDA1341_DATA0, &newreg, 1); ++ return 0; ++} ++ ++static int uda1341_update_indirect(struct l3_client *clnt, int cmd, void *arg) ++{ ++ struct uda1341 *uda = clnt->driver_data; ++ struct l3_gain *gain = arg; ++ struct l3_agc *agc = arg; ++ char buf[8], *p = buf; ++ int val, ret = 0; ++ ++ switch (cmd) { ++ case L3_SET_GAIN: ++ val = 31 - (gain->left * 31 / 100); ++ switch (gain->channel) { ++ case 1: ++ uda->regs.ext0 = EXT0_CH1_GAIN(val); ++ ADD_EXTFIELD(EXT0, ext0); ++ break; ++ ++ case 2: ++ uda->regs.ext1 = EXT1_CH2_GAIN(val); ++ ADD_EXTFIELD(EXT1, ext1); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ } ++ break; ++ ++ case L3_INPUT_AGC: ++ if (agc->channel == 2) { ++ if (agc->enable) ++ uda->regs.ext4 |= EXT4_AGC_ENABLE; ++ else ++ uda->regs.ext4 &= ~EXT4_AGC_ENABLE; ++#if 0 ++ agc->level ++ agc->attack ++ agc->decay ++#endif ++ ADD_EXTFIELD(EXT4, ext4); ++ } else ++ ret = -EINVAL; ++ break; ++ ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ if (ret == 0 && uda->active) ++ l3_write(clnt, UDA1341_DATA0, buf, p - buf); ++ ++ return ret; ++} ++ ++static int uda1341_mixer_ioctl(struct l3_client *clnt, int cmd, void *arg) ++{ ++ struct uda1341 *uda = clnt->driver_data; ++ struct l3_gain gain; ++ int val, nr = _IOC_NR(cmd), ret = 0; ++ ++ if (cmd == SOUND_MIXER_INFO) { ++ struct mixer_info mi; ++ ++ strncpy(mi.id, "UDA1341", sizeof(mi.id)); ++ strncpy(mi.name, "Philips UDA1341", sizeof(mi.name)); ++ mi.modify_counter = uda->mod_cnt; ++ return copy_to_user(arg, &mi, sizeof(mi)); ++ } ++ ++ if (_IOC_DIR(cmd) & _IOC_WRITE) { ++ ret = get_user(val, (int *)arg); ++ if (ret) ++ goto out; ++ ++ gain.left = val & 255; ++ gain.right = val >> 8; ++ gain.channel = 0; ++ ++ switch (nr) { ++ case SOUND_MIXER_VOLUME: ++ uda->volume = val; ++ uda->mod_cnt++; ++ uda1341_update_direct(clnt, L3_SET_VOLUME, &gain); ++ break; ++ ++ case SOUND_MIXER_BASS: ++ uda->bass = val; ++ uda->mod_cnt++; ++ uda1341_update_direct(clnt, L3_SET_BASS, &gain); ++ break; ++ ++ case SOUND_MIXER_TREBLE: ++ uda->treble = val; ++ uda->mod_cnt++; ++ uda1341_update_direct(clnt, L3_SET_TREBLE, &gain); ++ break; ++ ++ case SOUND_MIXER_LINE: ++ uda->line = val; ++ gain.channel = 1; ++ uda->mod_cnt++; ++ uda1341_update_indirect(clnt, L3_SET_GAIN, &gain); ++ break; ++ ++ case SOUND_MIXER_MIC: ++ uda->mic = val; ++ gain.channel = 2; ++ uda->mod_cnt++; ++ uda1341_update_indirect(clnt, L3_SET_GAIN, &gain); ++ break; ++ ++ case SOUND_MIXER_RECSRC: ++ break; ++ ++ default: ++ ret = -EINVAL; ++ } ++ } ++ ++ if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { ++ int nr = _IOC_NR(cmd); ++ ret = 0; ++ ++ switch (nr) { ++ case SOUND_MIXER_VOLUME: val = uda->volume; break; ++ case SOUND_MIXER_BASS: val = uda->bass; break; ++ case SOUND_MIXER_TREBLE: val = uda->treble; break; ++ case SOUND_MIXER_LINE: val = uda->line; break; ++ case SOUND_MIXER_MIC: val = uda->mic; break; ++ case SOUND_MIXER_RECSRC: val = REC_MASK; break; ++ case SOUND_MIXER_RECMASK: val = REC_MASK; break; ++ case SOUND_MIXER_DEVMASK: val = DEV_MASK; break; ++ case SOUND_MIXER_CAPS: val = 0; break; ++ case SOUND_MIXER_STEREODEVS: val = 0; break; ++ default: val = 0; ret = -EINVAL; break; ++ } ++ ++ if (ret == 0) ++ ret = put_user(val, (int *)arg); ++ } ++out: ++ return ret; ++} ++ ++static int uda1341_attach(struct l3_client *clnt) ++{ ++ struct uda1341 *uda; ++ ++ uda = kmalloc(sizeof(*uda), GFP_KERNEL); ++ if (!uda) ++ return -ENOMEM; ++ ++ memset(uda, 0, sizeof(*uda)); ++ ++ uda->volume = DEF_VOLUME | DEF_VOLUME << 8; ++ uda->bass = 50 | 50 << 8; ++ uda->treble = 50 | 50 << 8; ++ uda->line = 88 | 88 << 8; ++ uda->mic = 88 | 88 << 8; ++ ++ uda->regs.stat0 = STAT0_SC_256FS | STAT0_IF_LSB16; ++ uda->regs.stat1 = STAT1_DAC_GAIN | STAT1_ADC_GAIN | ++ STAT1_ADC_ON | STAT1_DAC_ON; ++ uda->regs.data0_0 = DATA0_VOLUME(62 - ((DEF_VOLUME * 61) / 100)); ++ uda->regs.data0_1 = DATA1_BASS(0) | DATA1_TREBLE(0); ++ uda->regs.data0_2 = DATA2_PEAKAFTER | DATA2_DEEMP_NONE | ++ DATA2_FILTER_MAX; ++ uda->regs.ext0 = EXT0_CH1_GAIN(4); ++ uda->regs.ext1 = EXT1_CH2_GAIN(4); ++ uda->regs.ext2 = EXT2_MIXMODE_MIX | EXT2_MIC_GAIN(4); ++ uda->regs.ext4 = EXT4_AGC_ENABLE | EXT4_INPUT_GAIN(0); ++ uda->regs.ext5 = EXT5_INPUT_GAIN(0); ++ uda->regs.ext6 = EXT6_AGC_CONSTANT(3) | EXT6_AGC_LEVEL(0); ++ ++ clnt->driver_data = uda; ++ ++ return 0; ++} ++ ++static void uda1341_detach(struct l3_client *clnt) ++{ ++ kfree(clnt->driver_data); ++} ++ ++static int ++uda1341_command(struct l3_client *clnt, int cmd, void *arg) ++{ ++ int ret = -EINVAL; ++ ++ if (_IOC_TYPE(cmd) == 'M') ++ ret = uda1341_mixer_ioctl(clnt, cmd, arg); ++ else if (cmd == L3_UDA1341_CONFIGURE) ++ ret = uda1341_configure(clnt, arg); ++ ++ return ret; ++} ++ ++static int uda1341_open(struct l3_client *clnt) ++{ ++ uda1341_cmd_init(clnt); ++ return 0; ++} ++ ++static void uda1341_close(struct l3_client *clnt) ++{ ++ struct uda1341 *uda = clnt->driver_data; ++ uda->active = 0; ++} ++ ++static struct l3_ops uda1341_ops = { ++ open: uda1341_open, ++ command: uda1341_command, ++ close: uda1341_close, ++}; ++ ++static struct l3_driver uda1341 = { ++ name: UDA1341_NAME, ++ attach_client: uda1341_attach, ++ detach_client: uda1341_detach, ++ ops: &uda1341_ops, ++ owner: THIS_MODULE, ++}; ++ ++static int __init uda1341_init(void) ++{ ++ return l3_add_driver(&uda1341); ++} ++ ++static void __exit uda1341_exit(void) ++{ ++ l3_del_driver(&uda1341); ++} ++ ++module_init(uda1341_init); ++module_exit(uda1341_exit); ++ ++MODULE_AUTHOR("Nicolas Pitre"); ++MODULE_DESCRIPTION("Philips UDA1341 CODEC driver"); +diff -urN linux-2.4.26/drivers/sound/vidc.c linux-2.4.26-vrs1/drivers/sound/vidc.c +--- linux-2.4.26/drivers/sound/vidc.c 2001-10-11 17:43:30.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/sound/vidc.c 2004-01-14 21:32:26.000000000 +0000 +@@ -40,6 +40,7 @@ + #endif + + #define VIDC_SOUND_CLOCK (250000) ++#define VIDC_SOUND_CLOCK_EXT (176400) + + /* + * When using SERIAL SOUND mode (external DAC), the number of physical +@@ -192,28 +193,43 @@ + return vidc_audio_format; + } + ++#define my_abs(i) ((i)<0 ? -(i) : (i)) ++ + static int vidc_audio_set_speed(int dev, int rate) + { + if (rate) { +- unsigned int hwctrl, hwrate; ++ unsigned int hwctrl, hwrate, hwrate_ext, rate_int, rate_ext; + unsigned int newsize, new2size; + +- /* +- * If we have selected 44.1kHz, use the DAC clock. +- */ +- if (0 && rate == 44100) { +- hwctrl = 0x00000002; +- hwrate = 3; +- } else { +- hwctrl = 0x00000003; ++ hwctrl = 0x00000003; + +- hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1; +- if (hwrate < 3) +- hwrate = 3; +- if (hwrate > 255) +- hwrate = 255; ++ /* Using internal clock */ ++ hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1; ++ if (hwrate < 3) ++ hwrate = 3; ++ if (hwrate > 255) ++ hwrate = 255; + +- rate = VIDC_SOUND_CLOCK / hwrate; ++ /* Using exernal clock */ ++ hwrate_ext = (((VIDC_SOUND_CLOCK_EXT * 2) / rate) + 1) >> 1; ++ if (hwrate_ext < 3) ++ hwrate_ext = 3; ++ if (hwrate_ext > 255) ++ hwrate_ext = 255; ++ ++ rate_int = VIDC_SOUND_CLOCK / hwrate; ++ rate_ext = VIDC_SOUND_CLOCK_EXT / hwrate_ext; ++ ++ /* Chose between external and internal clock */ ++ if (my_abs(rate_ext-rate) < my_abs(rate_int-rate)) { ++ /*printk("VIDC: external %d %d %d\n", rate, rate_ext, hwrate_ext);*/ ++ hwrate=hwrate_ext; ++ hwctrl=0x00000002; ++ rate=rate_ext; ++ } else { ++ /*printk("VIDC: internal %d %d %d\n", rate, rate_int, hwrate);*/ ++ hwctrl=0x00000003; ++ rate=rate_int; + } + + vidc_writel(0xb0000000 | (hwrate - 2)); +@@ -225,13 +241,14 @@ + if (newsize > 4096) + newsize = 4096; + for (new2size = 128; new2size < newsize; new2size <<= 1); +- if (new2size - newsize > newsize - (new2size >> 1)) +- new2size >>= 1; ++ if (new2size - newsize > newsize - (new2size >> 1)) ++ new2size >>= 1; + if (new2size > 4096) { + printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n", + newsize, new2size); + new2size = 4096; + } ++ /*printk("VIDC: dma size %d\n", new2size);*/ + dma_bufsize = new2size; + vidc_audio_rate = rate; + } +diff -urN linux-2.4.26/drivers/sound/waveartist.c linux-2.4.26-vrs1/drivers/sound/waveartist.c +--- linux-2.4.26/drivers/sound/waveartist.c 2001-10-25 21:53:52.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/sound/waveartist.c 2004-01-14 21:32:26.000000000 +0000 +@@ -247,17 +247,15 @@ + printk("\n"); + } + +- if (inb(io_base + STATR) & CMD_RF) { +- int old_data; +- +- /* flush the port +- */ ++ /* ++ * flush any stale command data from the port. ++ */ ++ while (inb(io_base + STATR) & CMD_RF) { ++ unsigned int old_data; + + old_data = inw(io_base + CMDR); +- +- if (debug_flg & DEBUG_CMD) +- printk("flushed %04X...", old_data); +- ++ printk("waveartist: flushing stale command data: 0x%04x pc=%p\n", ++ old_data, __builtin_return_address(0)); + udelay(10); + } + +@@ -287,16 +285,19 @@ + resp[i] = inw(io_base + CMDR); + } + +- if (debug_flg & DEBUG_CMD) { +- if (!timed_out) { +- printk("waveartist_cmd: resp="); +- +- for (i = 0; i < nr_resp; i++) +- printk("%04X ", resp[i]); +- +- printk("\n"); +- } else +- printk("waveartist_cmd: timed out\n"); ++ if (debug_flg & DEBUG_CMD && !timed_out) { ++ printk("waveartist_cmd: resp="); ++ ++ for (i = 0; i < nr_resp; i++) ++ printk("%04X ", resp[i]); ++ printk("\n"); ++ } ++ ++ if (timed_out) { ++ printk(KERN_ERR "waveartist_cmd: command timed out:"); ++ for (i = 0; i < nr_cmd; i++) ++ printk(" %04x", cmd[i]); ++ printk("\n"); + } + + return timed_out ? 1 : 0; +@@ -495,7 +496,6 @@ + audio_devs[dev]->flags & DMA_AUTOMODE && + intrflag && + count == devc->xfer_count) { +- devc->audio_mode |= PCM_ENABLE_INPUT; + return; /* + * Auto DMA mode on. No need to react + */ +@@ -571,9 +571,6 @@ + wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc; + unsigned int speed, bits; + +- if (devc->audio_mode) +- return 0; +- + speed = waveartist_get_speed(portc); + bits = waveartist_get_bits(portc); + +diff -urN linux-2.4.26/drivers/ssi/Config.in linux-2.4.26-vrs1/drivers/ssi/Config.in +--- linux-2.4.26/drivers/ssi/Config.in 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ssi/Config.in 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,11 @@ ++ ++mainmenu_option next_comment ++comment 'Synchronous Serial Interface' ++tristate 'Synchronous Serial Interface Support' CONFIG_SSI ++ ++comment 'SSI Bus Drivers' ++dep_tristate ' CLPS711X SSI support' CONFIG_SSI_CLPS711X $CONFIG_SSI $CONFIG_ARCH_CLPS711X ++ ++comment 'SSI Device Drivers' ++dep_tristate ' JUNO keyboard support' CONFIG_SSI_JUNO $CONFIG_SSI ++endmenu +diff -urN linux-2.4.26/drivers/ssi/Makefile linux-2.4.26-vrs1/drivers/ssi/Makefile +--- linux-2.4.26/drivers/ssi/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ssi/Makefile 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,50 @@ ++# ++# Makefile for the SSI drivers ++# ++# Note! Dependencies are done automagically by 'make dep', which also ++# removes any old dependencies. DON'T put your own dependencies here ++# unless it's something special (ie not a .c file). ++# ++# Note 2! The CFLAGS definition is now inherited from the ++# parent makefile. ++# ++ ++O_TARGET := ssi.o ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++export-objs := ++list-multi := ++ ++obj-$(CONFIG_SSI) += ssi_core.o ++obj-$(CONFIG_SSI_CLPS711X) += clps711x_ssi1.o ++obj-y += juno.o ++ ++# Extract lists of the multi-part drivers. ++# The 'int-*' lists are intermediate files used to build the multi's. ++ ++multi-y := $(filter $(list-multi), $(obj-y)) ++multi-m := $(filter $(list-multi), $(obj-m)) ++int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) ++int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) ++ ++# Files that are both resident and modular; remove from modular. ++ ++obj-m := $(filter-out $(obj-y), $(obj-m)) ++int-m := $(filter-out $(int-y), $(int-m)) ++ ++# Take multi-part drivers out of obj-y and put components in. ++ ++obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) ++ ++# Translate to Rules.make lists. ++ ++O_OBJS := $(filter-out $(export-objs), $(obj-y)) ++OX_OBJS := $(filter $(export-objs), $(obj-y)) ++M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) ++MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) ++ ++include $(TOPDIR)/Rules.make +diff -urN linux-2.4.26/drivers/ssi/README linux-2.4.26-vrs1/drivers/ssi/README +--- linux-2.4.26/drivers/ssi/README 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ssi/README 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,86 @@ ++ Synchronous Serial Interface bus driver ++ --------------------------------------- ++ ++ EEEEE X X PPPP EEEEE RRRR IIIII M M EEEEE N N TTTTT AAA L ++ E X X P P E R R I MM MM E NN N T A A L ++ EEEE X PPPP EEEE RRRR I M M M EEEE N N N T AAAAA L ++ E X X P E R R I M M E N NN T A A L ++ EEEEE X X P EEEEE R R IIIII M M EEEEE N N T A A LLLLL ++ ++This directory holds the SSI bus drivers. Basically, a SSI bus consists ++of the following signals: ++ ++ stxd Transmit data ++ srxd Receive data ++ sclk Clock ++ sfrm Frame ++ Chip selects (1 - n) ++ ++There may be more than one device on a SSI bus, and each device can ++have different timing requirements. There are several frame formats: ++ ++1. Texas Instruments Synchronous Serial Frame format ++ ++ sclk ____~_~_~_~_~_~_~_~____ ++ sfrm ____~~_________________ ++ stxd ------bn..........b0--- ++ srxd ------bn..........b0--- ++ ++ - data latched in on the falling edge of the clock ++ - data shifted out on the rising edge of the clock ++ ++2. Motorola SPI frame format ++ ++ sclk ______~_~_~_~_~_~_~____ ++ sfrm ~~~~________________~~~ ++ stxd -----bn..........b0---- ++ srxd ----.bn..........b0---- ++ ++ - data latched in on the rising edge of the clock ++ - data shifted out on the falling edge of the clock, or falling edge ++ of sfrm ++ ++3. National Microwire format ++ ++ sclk ______~_~_~_~_~_~_~_~_~_~_~_~_~_____ ++ sfrm ~~~~_____________________________~~~ ++ stxd -----bn......b0--------------------- ++ srxd -----------------bn..........b0.---- ++ ++ - data latched in on the rising edge of the clock ++ - data shifted out on the falling edge of the clock ++ - half duplex, one clock between transmission and reception ++ ++Types of devices ++---------------- ++ ++The following types of devices can be found on a SSP bus: ++ ++ Sound chips ++ Keyboard devices ++ Touch screen devices ++ ++Keyboard ++-------- ++ ++TX: ++0. Format: cfglen = 8, framelen = 8, clkpol = 1, clk < 250kHz ++1. select device ++2. keyboard responds asserting key_atn ++3. wait 0.1ms to 5ms ++4. transmit data byte ++5. wait >= 150us ++6. repeat step 4 until all data sent ++7. deselect device ++8. keyboard responds de-asserting key_atn ++9. wait >= 120us ++ ++RX: ++0. Format: cfglen = 8, framelen = 8, clkpol = 1, clk < 250kHz ++1. keyboard asserts key_atn ++2. select device after 0.1ms < 5ms ++3. read data byte ++4. wait 150us ++5. if key_atn still asserted, goto 3 ++6. deselect device ++7. wait >= 120us +diff -urN linux-2.4.26/drivers/ssi/clps711x_ssi1.c linux-2.4.26-vrs1/drivers/ssi/clps711x_ssi1.c +--- linux-2.4.26/drivers/ssi/clps711x_ssi1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ssi/clps711x_ssi1.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,237 @@ ++/* ++ * linux/drivers/ssi/clps711x_ssi1.c ++ * ++ * SSI bus driver for the CLPS711x SSI1 bus. We support EP7212 ++ * extensions as well. ++ * ++ * Frame sizes can be between 4 and 24 bits. ++ * Config sizes can be between 4 and 16 bits. ++ */ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "ssi_bus.h" ++#include "ssi_dev.h" ++ ++#define EP7212 ++ ++/* ++ * Port E on the P720T is used for the SPI bus chip selects ++ * 0 - Keyboard ++ * 1 - Touch screen ++ * 2 - CS4224 ADC/DAC ++ * 7 - idle ++ */ ++ ++#if 0 ++/* ++ * we place in the transmit buffer: ++ * ++ * received data (binary): ++ * 0xxxxxxxxxxxx000 ++ * where 'x' is the value ++ */ ++struct ssi_dev ads7846_dev = { ++ name: "ADS7846", ++ id: 1, ++ proto: SSI_SPI, ++ cfglen: 8, ++ framelen: 24, ++ clkpol: 0, ++ clkfreq: 2500000, ++}; ++ ++/* ++ * we place in the transmit buffer: ++ * write: <20> ... ++ * received data discarded ++ */ ++struct ssi_dev cs4224_dev = { ++ name: "CS4224", ++ id: 2, ++ proto: SSI_SPI, ++ cfglen: 8, ++ framelen: 8, ++ clkpol: 0, ++ clkfreq: 6000000, ++}; ++#endif ++ ++/* ++ * Supplement with whatever method your board requires ++ */ ++static void ssi1_select_id(int id) ++{ ++ if (machine_is_p720t()) { ++ clps_writel(7, PEDDR); ++ clps_writel(id, PEDR); ++ } ++} ++ ++/* ++ * Select the specified device. The upper layers will have already ++ * checked that the bus transmit queue is idle. We need to make sure ++ * that the interface itself is idle. ++ */ ++static int ssi1_select(struct ssi_bus *bus, struct ssi_dev *dev) ++{ ++ u_int id = dev ? dev->id : 7; ++ u_int val; ++ ++ /* ++ * Make sure that the interface is idle ++ */ ++ do { ++ val = clps_readl(SYSFLG1); ++ } while (val & SYSFLG1_SSIBUSY); ++ ++ ssi1_select_id(7); ++ ++ if (dev) { ++ /* ++ * Select clock frequency. This is very rough, ++ * and assumes that we're operating in PLL mode. ++ */ ++ val = clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK; ++// if (dev->clkfreq <= 16000) /* <16kHz */ ++// val |= SYSCON1_ADCKSEL(0); ++// else if (dev->clkfreq < 64000) /* <64kHz */ ++// val |= SYSCON1_ADCKSEL(1); ++// else if (dev->clkfreq < 128000) /* <128kHz */ ++ val |= SYSCON1_ADCKSEL(2); ++// else /* >= 128kHz */ ++// val |= SYSCON1_ADCKSEL(3); ++ clps_writel(val, SYSCON1); ++ ++ bus->cfglen = dev->cfglen; ++ bus->framelen = dev->framelen; ++ bus->clkpol = dev->clkpol; ++ bus->proto = dev->proto; ++ ++#ifdef EP7212 ++ /* ++ * Set the clock edge according to the device. ++ * (set clkpol if the device reads data on the ++ * falling edge of the clock signal). ++ */ ++ val = ep_readl(SYSCON3) & ~SYSCON3_ADCCKNSEN; ++ if (bus->clkpol && dev->proto != SSI_USAR) ++ val |= SYSCON3_ADCCKNSEN; ++ ep_writel(val, SYSCON3); ++#endif ++ ++ /* ++ * Select the device ++ */ ++ ssi1_select_id(id); ++ ++#ifdef EP7212 ++ /* ++ * If we are doing USAR, wait 30us, then set ++ * the clock line low. ++ */ ++ if (dev->proto == SSI_USAR) { ++ udelay(150); ++ ++ val |= SYSCON3_ADCCKNSEN; ++ ep_writel(val, SYSCON3); ++ } ++#endif ++ } ++ ++ return 0; ++} ++ ++static void ssi1_int(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct ssi_bus *bus = (struct ssi_bus *)dev_id; ++ ++ /* ++ * Read the data word and queue it. ++ */ ++ ssi_core_rcv(bus, clps_readl(SYNCIO)); ++} ++ ++/* ++ * Enable transmission and/or of some bytes ++ */ ++static int ssi1_trans(struct ssi_bus *bus, u_int data) ++{ ++ u_int syncio; ++ ++#ifdef EP7212 ++ data <<= 32 - bus->cfglen; ++ syncio = bus->cfglen | bus->framelen << 7 | data; ++#else ++ syncio = data | bus->framelen << 8; ++#endif ++ ++ clps_writel(syncio, SYNCIO); ++ clps_writel(syncio | SYNCIO_TXFRMEN, SYNCIO); ++ return 0; ++} ++ ++/* ++ * Initialise the SSI bus. ++ */ ++static int ssi1_bus_init(struct ssi_bus *bus) ++{ ++ int retval, val; ++ ++ retval = request_irq(IRQ_SSEOTI, ssi1_int, 0, "ssi1", bus); ++ if (retval) ++ return retval; ++ ++#ifdef EP7212 ++ /* ++ * EP7212 only! Set the configuration command length. ++ * On the CLPS711x chips, it is fixed at 8 bits. ++ */ ++ val = ep_readl(SYSCON3); ++ val |= SYSCON3_ADCCON; ++ ep_writel(val, SYSCON3); ++#endif ++ ++ ssi1_select(bus, NULL); ++ ++ PLD_SPI |= PLD_SPI_EN; ++ ++ return 0; ++} ++ ++static void ssi1_bus_exit(struct ssi_bus *bus) ++{ ++ ssi1_select(bus, NULL); ++ ++ PLD_SPI &= ~PLD_SPI_EN; ++ ++ free_irq(IRQ_SSEOTI, bus); ++} ++ ++struct ssi_bus clps711x_ssi1_bus = { ++ name: "clps711x_ssi1", ++ init: ssi1_bus_init, ++ exit: ssi1_bus_exit, ++ select: ssi1_select, ++ trans: ssi1_trans, ++}; ++ ++static int __init clps711x_ssi1_init(void) ++{ ++ return ssi_register_bus(&clps711x_ssi1_bus); ++} ++ ++static void __exit clps711x_ssi1_exit(void) ++{ ++ ssi_unregister_bus(&clps711x_ssi1_bus); ++} ++ ++module_init(clps711x_ssi1_init); ++module_exit(clps711x_ssi1_exit); +diff -urN linux-2.4.26/drivers/ssi/juno.c linux-2.4.26-vrs1/drivers/ssi/juno.c +--- linux-2.4.26/drivers/ssi/juno.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ssi/juno.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,131 @@ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#include "ssi_bus.h" ++#include "ssi_dev.h" ++ ++extern struct ssi_bus clps711x_ssi1_bus; ++ ++static u_int recvbuf[16]; ++static volatile u_int ptr, rxed; ++ ++static inline void juno_enable_irq(void) ++{ ++ enable_irq(IRQ_EINT1); ++} ++ ++static inline void juno_disable_irq(void) ++{ ++ disable_irq(IRQ_EINT1); ++} ++ ++static void juno_rcv(struct ssi_dev *dev, u_int data) ++{ ++ if (ptr < 16) { ++ recvbuf[ptr] = data; ++ ptr++; ++ } else ++ printk("juno_rcv: %04x\n", data); ++ rxed = 1; ++} ++ ++static void juno_irq(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct ssi_dev *dev = dev_id; ++ ++ printk("juno_irq\n"); ++ ++ ssi_select_device(dev->bus, dev); ++ ++ ptr = 0; ++ do { ++ rxed = 0; ++ ssi_transmit_data(dev, 0xff); ++ while (rxed == 0); ++ udelay(150); ++ } while (PLD_INT & PLD_INT_KBD_ATN); ++ ++ ssi_select_device(dev->bus, NULL); ++ ++ { int i; ++ printk("juno_rcv: "); ++ for (i = 0; i < ptr; i++) ++ printk("%04x ", recvbuf[i]); ++ printk("\n"); ++ } ++} ++ ++static void juno_command(struct ssi_dev *dev, int cmd, int data) ++{ ++ ssi_transmit_data(dev, cmd); ++ mdelay(1); ++ ssi_transmit_data(dev, data); ++ mdelay(1); ++ ssi_transmit_data(dev, 0xa0 ^ 0xc0); ++ mdelay(1); ++} ++ ++static int juno_dev_init(struct ssi_dev *dev) ++{ ++ int retval; ++ ++ PLD_KBD |= PLD_KBD_EN; ++ ptr = 16; ++ ++ mdelay(20); ++ ++ retval = request_irq(IRQ_EINT1, juno_irq, 0, dev->name, dev); ++ if (retval) ++ return retval; ++ ++ juno_disable_irq(); ++ ++ if (ssi_select_device(dev->bus, dev) != 0) { ++ printk("juno: ssi_select_dev failed\n"); ++ return -EBUSY; ++ } ++ ++ mdelay(1); ++ ++ juno_command(dev, 0x80, 0x20); ++ ++ ssi_select_device(dev->bus, NULL); ++ ++ juno_enable_irq(); ++ ++ return 0; ++} ++ ++static struct ssi_dev juno_dev = { ++ name: "Juno", ++ id: 0, ++ proto: SSI_USAR, ++ cfglen: 8, ++ framelen: 8, ++ clkpol: 1, ++ clkfreq: 250000, ++ rcv: juno_rcv, ++ init: juno_dev_init, ++}; ++ ++static int __init juno_init(void) ++{ ++ return ssi_register_device(&clps711x_ssi1_bus, &juno_dev); ++} ++ ++static void __exit juno_exit(void) ++{ ++ ssi_unregister_device(&juno_dev); ++} ++ ++module_init(juno_init); ++module_exit(juno_exit); ++ +diff -urN linux-2.4.26/drivers/ssi/ssi_bus.h linux-2.4.26-vrs1/drivers/ssi/ssi_bus.h +--- linux-2.4.26/drivers/ssi/ssi_bus.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ssi/ssi_bus.h 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,21 @@ ++#include ++ ++struct ssi_dev; ++ ++struct ssi_bus { ++ u_char cfglen; ++ u_char framelen; ++ u_char clkpol; ++ u_char proto; ++ struct ssi_dev *dev; /* current device */ ++ int (*select)(struct ssi_bus *, struct ssi_dev *); ++ int (*trans)(struct ssi_bus *, u_int data); ++ int (*init)(struct ssi_bus *); ++ void (*exit)(struct ssi_bus *); ++ char *name; ++ u_int devices; ++}; ++ ++extern int ssi_core_rcv(struct ssi_bus *bus, u_int data); ++extern int ssi_register_bus(struct ssi_bus *bus); ++extern int ssi_unregister_bus(struct ssi_bus *bus); +diff -urN linux-2.4.26/drivers/ssi/ssi_core.c linux-2.4.26-vrs1/drivers/ssi/ssi_core.c +--- linux-2.4.26/drivers/ssi/ssi_core.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ssi/ssi_core.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,175 @@ ++/* ++ * linux/drivers/ssi/ssi_core.c ++ * ++ * This file provides a common framework to allow multiple SSI devices ++ * to work together on a single SSI bus. ++ * ++ * You can use this in two ways: ++ * 1. select the device, queue up data, flush the data to the device, ++ * (optionally) purge the received data, deselect the device. ++ * 2. select the device, queue up one data word, flush to the device ++ * read data word, queue up next data word, flush to the device... ++ * deselect the device. ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "ssi_bus.h" ++#include "ssi_dev.h" ++ ++#define DEBUG ++ ++/** ++ * ssi_core_rcv - pass received SSI data to the device ++ * @bus: the bus that the data was received from ++ * @data: the data word that was received ++ * ++ * This function is intended to be called by SSI bus device ++ * drivers to pass received data to the device driver. ++ */ ++int ssi_core_rcv(struct ssi_bus *bus, u_int data) ++{ ++ struct ssi_dev *dev = bus->dev; ++ ++ if (dev && dev->rcv) ++ dev->rcv(dev, data); ++ ++ return 0; ++} ++ ++/** ++ * ssi_transmit_data - queue SSI data for later transmission ++ * @dev: device requesting data to be transmitted ++ * @data: data word to be transmitted. ++ * ++ * Queue one data word of SSI data for later transmission. ++ */ ++int ssi_transmit_data(struct ssi_dev *dev, u_int data) ++{ ++ struct ssi_bus *bus = dev->bus; ++ ++ /* ++ * Make sure that we currently own the bus ++ */ ++ if (bus->dev != dev) ++ BUG(); ++ ++ bus->trans(bus, data); ++ return 0; ++} ++ ++/** ++ * ssi_select_device - select a SSI device for later transactions ++ * @dev: device to be selected ++ */ ++int ssi_select_device(struct ssi_bus *bus, struct ssi_dev *dev) ++{ ++ int retval; ++ ++#ifdef DEBUG ++ printk("SSI: selecting device %s on bus %s\n", ++ dev ? dev->name : "", bus->name); ++#endif ++ ++ /* ++ * Select the device if it wasn't already selected. ++ */ ++ retval = 0; ++ if (bus->dev != dev) { ++ retval = bus->select(bus, dev); ++ bus->dev = dev; ++ } ++ ++ return retval; ++} ++ ++/** ++ * ssi_register_device - register a SSI device with a SSI bus ++ * @bus: bus ++ * @dev: SSI device ++ */ ++int ssi_register_device(struct ssi_bus *bus, struct ssi_dev *dev) ++{ ++ int retval; ++ ++ dev->bus = bus; ++ bus->devices++; ++ retval = dev->init(dev); ++ if (retval != 0) { ++ dev->bus = NULL; ++ bus->devices--; ++ } else { ++#ifdef DEBUG ++ printk("SSI: registered new device %s on bus %s\n", dev->name, bus->name); ++#endif ++ } ++ return retval; ++} ++ ++/** ++ * ssi_unregister_device - unregister a SSI device from a SSI bus ++ * @dev: SSI device ++ */ ++int ssi_unregister_device(struct ssi_dev *dev) ++{ ++ struct ssi_bus *bus = dev->bus; ++ ++ if (bus->dev == dev) ++ bus->dev = NULL; ++ ++ dev->bus = NULL; ++ bus->devices--; ++#ifdef DEBUG ++ printk("SSI: unregistered device %s on bus %s\n", dev->name, bus->name); ++#endif ++ return 0; ++} ++ ++/** ++ * ssi_register_bus - register a SSI bus driver ++ * @bus: bus ++ */ ++int ssi_register_bus(struct ssi_bus *bus) ++{ ++ int retval; ++ ++ retval = bus->init(bus); ++ if (retval == 0) { ++ bus->devices = 0; ++#ifdef DEBUG ++ printk("SSI: registered new bus %s\n", bus->name); ++#endif ++ } ++ ++ return retval; ++} ++ ++/** ++ * ssi_unregister_bus - unregister a SSI bus driver ++ * @bus: bus ++ */ ++int ssi_unregister_bus(struct ssi_bus *bus) ++{ ++ int retval = -EBUSY; ++ if (bus->devices == 0) { ++ retval = 0; ++ } ++ return retval; ++} ++ ++static int __init ssi_init(void) ++{ ++ return 0; ++} ++ ++static void __exit ssi_exit(void) ++{ ++} ++ ++module_init(ssi_init); ++module_exit(ssi_exit); +diff -urN linux-2.4.26/drivers/ssi/ssi_dev.h linux-2.4.26-vrs1/drivers/ssi/ssi_dev.h +--- linux-2.4.26/drivers/ssi/ssi_dev.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/ssi/ssi_dev.h 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,21 @@ ++struct ssi_bus; ++ ++#define SSI_SPI 1 ++#define SSI_MICROWIRE 2 ++#define SSI_TISSF 3 ++#define SSI_USAR 4 ++ ++struct ssi_dev { ++ char *name; ++ u_int id; ++ u_int clkfreq; ++ u_char cfglen; ++ u_char framelen; ++ u_char clkpol; ++ u_char proto; ++ void (*rcv)(struct ssi_dev *, u_int); ++ int (*init)(struct ssi_dev *); ++ struct ssi_bus *bus; ++}; ++ ++ +diff -urN linux-2.4.26/drivers/usb/Config.in linux-2.4.26-vrs1/drivers/usb/Config.in +--- linux-2.4.26/drivers/usb/Config.in 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/usb/Config.in 2004-02-23 13:36:33.000000000 +0000 +@@ -4,7 +4,13 @@ + mainmenu_option next_comment + comment 'USB support' + +-dep_tristate 'Support for USB' CONFIG_USB $CONFIG_PCI ++# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. ++if [ "$CONFIG_PCI" = "y" -o "$CONFIG_SA1111" = "y" -o "$CONFIG_ARCH_AT91RM9200" = "y" ]; then ++ tristate 'Support for USB' CONFIG_USB ++else ++ define_bool CONFIG_USB n ++fi ++ + if [ "$CONFIG_USB" = "y" -o "$CONFIG_USB" = "m" ]; then + bool ' USB verbose debug messages' CONFIG_USB_DEBUG + +diff -urN linux-2.4.26/drivers/usb/Makefile linux-2.4.26-vrs1/drivers/usb/Makefile +--- linux-2.4.26/drivers/usb/Makefile 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/usb/Makefile 2004-02-23 13:36:33.000000000 +0000 +@@ -74,6 +74,14 @@ + + subdir-$(CONFIG_USB_OHCI) += host + ifeq ($(CONFIG_USB_OHCI),y) ++ obj-y += host/usb-ohci.o host/usb-ohci-pci.o ++endif ++subdir-$(CONFIG_USB_OHCI_SA1111)+= host ++ifeq ($(CONFIG_USB_OHCI),y) ++ obj-y += host/usb-ohci.o host/usb-ohci-sa1111.o ++endif ++subdir-$(CONFIG_USB_OHCI_AT91) += host ++ifeq ($(CONFIG_USB_OHCI_AT91),y) + obj-y += host/usb-ohci.o + endif + +@@ -85,8 +93,6 @@ + obj-$(CONFIG_USB_KBD) += usbkbd.o + obj-$(CONFIG_USB_AIPTEK) += aiptek.o + obj-$(CONFIG_USB_WACOM) += wacom.o +-obj-$(CONFIG_USB_KBTAB) += kbtab.o +-obj-$(CONFIG_USB_POWERMATE) += powermate.o + + obj-$(CONFIG_USB_SCANNER) += scanner.o + obj-$(CONFIG_USB_ACM) += acm.o +diff -urN linux-2.4.26/drivers/usb/hcd.c linux-2.4.26-vrs1/drivers/usb/hcd.c +--- linux-2.4.26/drivers/usb/hcd.c 2004-04-19 11:44:27.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/usb/hcd.c 2004-04-19 10:22:01.000000000 +0100 +@@ -96,7 +96,7 @@ + /* used when updating hcd data */ + static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED; + +-static struct usb_operations hcd_operations; ++/*static*/ struct usb_operations hcd_operations; + + /*-------------------------------------------------------------------------*/ + +@@ -1208,13 +1208,21 @@ + } else { + if (usb_pipecontrol (urb->pipe)) + urb->setup_dma = pci_map_single ( ++#ifdef CONFIG_PCI + hcd->pdev, ++#else ++ NULL, ++#endif + urb->setup_packet, + sizeof (struct usb_ctrlrequest), + PCI_DMA_TODEVICE); + if (urb->transfer_buffer_length != 0) + urb->transfer_dma = pci_map_single ( ++#ifdef CONFIG_PCI + hcd->pdev, ++#else ++ NULL, ++#endif + urb->transfer_buffer, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) +@@ -1424,7 +1432,7 @@ + return 0; + } + +-static struct usb_operations hcd_operations = { ++/*static*/ struct usb_operations hcd_operations = { + allocate: hcd_alloc_dev, + get_frame_number: hcd_get_frame_number, + submit_urb: hcd_submit_urb, +@@ -1434,7 +1442,7 @@ + + /*-------------------------------------------------------------------------*/ + +-static void hcd_irq (int irq, void *__hcd, struct pt_regs * r) ++/*static*/ void hcd_irq (int irq, void *__hcd, struct pt_regs * r) + { + struct usb_hcd *hcd = __hcd; + int start = hcd->state; +@@ -1490,12 +1498,24 @@ + + /* For 2.4, don't unmap bounce buffer if it's a root hub operation. */ + if (usb_pipecontrol (urb->pipe) && !is_root_hub_operation) +- pci_unmap_single (hcd->pdev, urb->setup_dma, ++ pci_unmap_single ( ++#ifdef CONFIG_PCI ++ hcd->pdev, ++#else ++ NULL, ++#endif ++ urb->setup_dma, + sizeof (struct usb_ctrlrequest), + PCI_DMA_TODEVICE); + + if ((urb->transfer_buffer_length != 0) && !is_root_hub_operation) +- pci_unmap_single (hcd->pdev, urb->transfer_dma, ++ pci_unmap_single ( ++#ifdef CONFIG_PCI ++ hcd->pdev, ++#else ++ NULL, ++#endif ++ urb->transfer_dma, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE +diff -urN linux-2.4.26/drivers/usb/host/Config.in linux-2.4.26-vrs1/drivers/usb/host/Config.in +--- linux-2.4.26/drivers/usb/host/Config.in 2003-11-28 18:26:20.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/usb/host/Config.in 2004-02-11 01:35:27.000000000 +0000 +@@ -12,8 +12,13 @@ + define_bool CONFIG_USB_UHCI_ALT n + fi + dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB ++dep_tristate ' SA1111 OHCI-compatible host interface support' CONFIG_USB_OHCI_SA1111 $CONFIG_USB + if [ "$CONFIG_ARM" = "y" -o "$CONFIG_X86" = "y" -a "$CONFIG_X86_64" != "y" ]; then + # Cypress embedded USB controller on StrongARM or on x86 in PC/104 + dep_tristate ' SL811HS Alternate (x86, StrongARM, isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL + dep_tristate ' SL811HS (x86, StrongARM) support, old driver' CONFIG_USB_SL811HS $CONFIG_USB $CONFIG_EXPERIMENTAL + fi ++if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then ++ dep_tristate ' AT91RM9200 OHCI-compatible host interface support' CONFIG_USB_OHCI_AT91 $CONFIG_USB ++fi ++ +diff -urN linux-2.4.26/drivers/usb/host/Makefile linux-2.4.26-vrs1/drivers/usb/host/Makefile +--- linux-2.4.26/drivers/usb/host/Makefile 2003-11-28 18:26:20.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/usb/host/Makefile 2004-02-11 01:29:25.000000000 +0000 +@@ -8,9 +8,11 @@ + obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o + obj-$(CONFIG_USB_UHCI_ALT) += uhci.o + obj-$(CONFIG_USB_UHCI) += usb-uhci.o +-obj-$(CONFIG_USB_OHCI) += usb-ohci.o ++obj-$(CONFIG_USB_OHCI) += usb-ohci.o usb-ohci-pci.o + obj-$(CONFIG_USB_SL811HS_ALT) += sl811.o + obj-$(CONFIG_USB_SL811HS) += hc_sl811.o ++obj-$(CONFIG_USB_OHCI_SA1111) += usb-ohci.o usb-ohci-sa1111.o ++obj-$(CONFIG_USB_OHCI_AT91) += usb-ohci.o + + # Extract lists of the multi-part drivers. + # The 'int-*' lists are the intermediate files used to build the multi's. +diff -urN linux-2.4.26/drivers/usb/host/usb-ohci-pci.c linux-2.4.26-vrs1/drivers/usb/host/usb-ohci-pci.c +--- linux-2.4.26/drivers/usb/host/usb-ohci-pci.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/usb/host/usb-ohci-pci.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,436 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* for in_interrupt() */ ++#undef DEBUG ++#include ++ ++#include "usb-ohci.h" ++ ++#ifdef CONFIG_PMAC_PBOOK ++#include ++#include ++#include ++#ifndef CONFIG_PM ++#define CONFIG_PM ++#endif ++#endif ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Increment the module usage count, start the control thread and ++ * return success. */ ++ ++static struct pci_driver ohci_pci_driver; ++extern spinlock_t usb_ed_lock; ++int __devinit ++hc_add_ohci(struct pci_dev *dev, int irq, void *membase, unsigned long flags, ++ const char *name, const char *slot_name); ++extern void hc_remove_ohci(ohci_t *ohci); ++ ++static int __devinit ++hc_found_ohci (struct pci_dev *dev, int irq, ++ void *mem_base, const struct pci_device_id *id) ++{ ++ unsigned long flags = id->driver_data; ++ ++ printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name); ++ ++ /* Check for NSC87560. We have to look at the bridge (fn1) to identify ++ the USB (fn2). This quirk might apply to more or even all NSC stuff ++ I don't know.. */ ++ ++ if(dev->vendor == PCI_VENDOR_ID_NS) ++ { ++ struct pci_dev *fn1 = pci_find_slot(dev->bus->number, PCI_DEVFN(PCI_SLOT(dev->devfn), 1)); ++ if(fn1 && fn1->vendor == PCI_VENDOR_ID_NS && fn1->device == PCI_DEVICE_ID_NS_87560_LIO) ++ flags |= OHCI_QUIRK_SUCKYIO; ++ ++ } ++ ++ if (flags & OHCI_QUIRK_SUCKYIO) ++ printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n"); ++ if (flags & OHCI_QUIRK_AMD756) ++ printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n"); ++ ++ return hc_add_ohci(dev, irq, mem_base, flags, ++ ohci_pci_driver.name, dev->slot_name); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_PM ++ ++/* controller died; cleanup debris, then restart */ ++/* must not be called from interrupt context */ ++ ++static void hc_restart (ohci_t *ohci) ++{ ++ int temp; ++ int i; ++ ++ if (ohci->pci_latency) ++ pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency); ++ ++ ohci->disabled = 1; ++ ohci->sleeping = 0; ++ if (ohci->bus->root_hub) ++ usb_disconnect (&ohci->bus->root_hub); ++ ++ /* empty the interrupt branches */ ++ for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0; ++ for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0; ++ ++ /* no EDs to remove */ ++ ohci->ed_rm_list [0] = NULL; ++ ohci->ed_rm_list [1] = NULL; ++ ++ /* empty control and bulk lists */ ++ ohci->ed_isotail = NULL; ++ ohci->ed_controltail = NULL; ++ ohci->ed_bulktail = NULL; ++ ++ if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { ++ err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp); ++ } else ++ dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name); ++} ++ ++#endif /* CONFIG_PM */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* configured so that an OHCI device is always provided */ ++/* always called with process context; sleeping is OK */ ++ ++static int __devinit ++ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) ++{ ++ unsigned long mem_resource, mem_len; ++ void *mem_base; ++ int status; ++ ++ if (pci_enable_device(dev) < 0) ++ return -ENODEV; ++ ++ if (!dev->irq) { ++ err("found OHCI device with no IRQ assigned. check BIOS settings!"); ++ pci_disable_device (dev); ++ return -ENODEV; ++ } ++ ++ /* we read its hardware registers as memory */ ++ mem_resource = pci_resource_start(dev, 0); ++ mem_len = pci_resource_len(dev, 0); ++ if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) { ++ dbg ("controller already in use"); ++ pci_disable_device (dev); ++ return -EBUSY; ++ } ++ ++ mem_base = ioremap_nocache (mem_resource, mem_len); ++ if (!mem_base) { ++ err("Error mapping OHCI memory"); ++ release_mem_region (mem_resource, mem_len); ++ pci_disable_device (dev); ++ return -EFAULT; ++ } ++ ++ /* controller writes into our memory */ ++ pci_set_master (dev); ++ ++ status = hc_found_ohci (dev, dev->irq, mem_base, id); ++ if (status < 0) { ++ iounmap (mem_base); ++ release_mem_region (mem_resource, mem_len); ++ pci_disable_device (dev); ++ } ++ return status; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* may be called from interrupt context [interface spec] */ ++/* may be called without controller present */ ++/* may be called with controller, bus, and devices active */ ++ ++static void __devexit ++ohci_pci_remove (struct pci_dev *dev) ++{ ++ ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev); ++ ++ dbg ("remove %s controller usb-%s%s%s", ++ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), ++ dev->slot_name, ++ ohci->disabled ? " (disabled)" : "", ++ in_interrupt () ? " in interrupt" : "" ++ ); ++ ++ hc_remove_ohci(ohci); ++ ++ release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0)); ++ pci_disable_device (dev); ++} ++ ++ ++#ifdef CONFIG_PM ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int ++ohci_pci_suspend (struct pci_dev *dev, u32 state) ++{ ++ ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev); ++ unsigned long flags; ++ u16 cmd; ++ ++ if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) { ++ dbg ("can't suspend usb-%s (state is %s)", dev->slot_name, ++ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS)); ++ return -EIO; ++ } ++ ++ /* act as if usb suspend can always be used */ ++ info ("USB suspend: usb-%s", dev->slot_name); ++ ohci->sleeping = 1; ++ ++ /* First stop processing */ ++ spin_lock_irqsave (&usb_ed_lock, flags); ++ ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); ++ writel (ohci->hc_control, &ohci->regs->control); ++ writel (OHCI_INTR_SF, &ohci->regs->intrstatus); ++ (void) readl (&ohci->regs->intrstatus); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); ++ ++ /* Wait a frame or two */ ++ mdelay(1); ++ if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF) ++ mdelay (1); ++ ++#ifdef CONFIG_PMAC_PBOOK ++ if (_machine == _MACH_Pmac) ++ disable_irq (ohci->irq); ++ /* else, 2.4 assumes shared irqs -- don't disable */ ++#endif ++ /* Enable remote wakeup */ ++ writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable); ++ ++ /* Suspend chip and let things settle down a bit */ ++ ohci->hc_control = OHCI_USB_SUSPEND; ++ writel (ohci->hc_control, &ohci->regs->control); ++ (void) readl (&ohci->regs->control); ++ mdelay (500); /* No schedule here ! */ ++ switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) { ++ case OHCI_USB_RESET: ++ dbg("Bus in reset phase ???"); ++ break; ++ case OHCI_USB_RESUME: ++ dbg("Bus in resume phase ???"); ++ break; ++ case OHCI_USB_OPER: ++ dbg("Bus in operational phase ???"); ++ break; ++ case OHCI_USB_SUSPEND: ++ dbg("Bus suspended"); ++ break; ++ } ++ /* In some rare situations, Apple's OHCI have happily trashed ++ * memory during sleep. We disable it's bus master bit during ++ * suspend ++ */ ++ pci_read_config_word (dev, PCI_COMMAND, &cmd); ++ cmd &= ~PCI_COMMAND_MASTER; ++ pci_write_config_word (dev, PCI_COMMAND, cmd); ++#ifdef CONFIG_PMAC_PBOOK ++ { ++ struct device_node *of_node; ++ ++ /* Disable USB PAD & cell clock */ ++ of_node = pci_device_to_OF_node (ohci->ohci_dev); ++ if (of_node) ++ pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); ++ } ++#endif ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int ++ohci_pci_resume (struct pci_dev *dev) ++{ ++ ohci_t *ohci = (ohci_t *) pci_get_drvdata(dev); ++ int temp; ++ unsigned long flags; ++ ++ /* guard against multiple resumes */ ++ atomic_inc (&ohci->resume_count); ++ if (atomic_read (&ohci->resume_count) != 1) { ++ err ("concurrent PCI resumes for usb-%s", dev->slot_name); ++ atomic_dec (&ohci->resume_count); ++ return 0; ++ } ++ ++#ifdef CONFIG_PMAC_PBOOK ++ { ++ struct device_node *of_node; ++ ++ /* Re-enable USB PAD & cell clock */ ++ of_node = pci_device_to_OF_node (ohci->ohci_dev); ++ if (of_node) ++ pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1); ++ } ++#endif ++ ++ /* did we suspend, or were we powered off? */ ++ ohci->hc_control = readl (&ohci->regs->control); ++ temp = ohci->hc_control & OHCI_CTRL_HCFS; ++ ++#ifdef DEBUG ++ /* the registers may look crazy here */ ++ ohci_dump_status (ohci); ++#endif ++ ++ /* Re-enable bus mastering */ ++ pci_set_master(ohci->ohci_dev); ++ ++ switch (temp) { ++ ++ case OHCI_USB_RESET: // lost power ++ info ("USB restart: usb-%s", dev->slot_name); ++ hc_restart (ohci); ++ break; ++ ++ case OHCI_USB_SUSPEND: // host wakeup ++ case OHCI_USB_RESUME: // remote wakeup ++ info ("USB continue: usb-%s from %s wakeup", dev->slot_name, ++ (temp == OHCI_USB_SUSPEND) ++ ? "host" : "remote"); ++ ohci->hc_control = OHCI_USB_RESUME; ++ writel (ohci->hc_control, &ohci->regs->control); ++ (void) readl (&ohci->regs->control); ++ mdelay (20); /* no schedule here ! */ ++ /* Some controllers (lucent) need a longer delay here */ ++ mdelay (15); ++ temp = readl (&ohci->regs->control); ++ temp = ohci->hc_control & OHCI_CTRL_HCFS; ++ if (temp != OHCI_USB_RESUME) { ++ err ("controller usb-%s won't resume", dev->slot_name); ++ ohci->disabled = 1; ++ return -EIO; ++ } ++ ++ /* Some chips likes being resumed first */ ++ writel (OHCI_USB_OPER, &ohci->regs->control); ++ (void) readl (&ohci->regs->control); ++ mdelay (3); ++ ++ /* Then re-enable operations */ ++ spin_lock_irqsave (&usb_ed_lock, flags); ++ ohci->disabled = 0; ++ ohci->sleeping = 0; ++ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; ++ if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) { ++ if (ohci->ed_controltail) ++ ohci->hc_control |= OHCI_CTRL_CLE; ++ if (ohci->ed_bulktail) ++ ohci->hc_control |= OHCI_CTRL_BLE; ++ } ++ writel (ohci->hc_control, &ohci->regs->control); ++ writel (OHCI_INTR_SF, &ohci->regs->intrstatus); ++ writel (OHCI_INTR_SF, &ohci->regs->intrenable); ++ /* Check for a pending done list */ ++ writel (OHCI_INTR_WDH, &ohci->regs->intrdisable); ++ (void) readl (&ohci->regs->intrdisable); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); ++#ifdef CONFIG_PMAC_PBOOK ++ if (_machine == _MACH_Pmac) ++ enable_irq (ohci->irq); ++#endif ++ if (ohci->hcca->done_head) ++ dl_done_list (ohci, dl_reverse_done_list (ohci)); ++ writel (OHCI_INTR_WDH, &ohci->regs->intrenable); ++ writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ ++ writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ ++ break; ++ ++ default: ++ warn ("odd PCI resume for usb-%s", dev->slot_name); ++ } ++ ++ /* controller is operational, extra resumes are harmless */ ++ atomic_dec (&ohci->resume_count); ++ ++ return 0; ++} ++ ++#endif /* CONFIG_PM */ ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static const struct pci_device_id __devinitdata ohci_pci_ids [] = { { ++ ++ /* ++ * AMD-756 [Viper] USB has a serious erratum when used with ++ * lowspeed devices like mice. ++ */ ++ vendor: 0x1022, ++ device: 0x740c, ++ subvendor: PCI_ANY_ID, ++ subdevice: PCI_ANY_ID, ++ ++ driver_data: OHCI_QUIRK_AMD756, ++ ++} , { ++ ++ /* handle any USB OHCI controller */ ++ class: ((PCI_CLASS_SERIAL_USB << 8) | 0x10), ++ class_mask: ~0, ++ ++ /* no matter who makes it */ ++ vendor: PCI_ANY_ID, ++ device: PCI_ANY_ID, ++ subvendor: PCI_ANY_ID, ++ subdevice: PCI_ANY_ID, ++ ++ }, { /* end: all zeroes */ } ++}; ++ ++MODULE_DEVICE_TABLE (pci, ohci_pci_ids); ++ ++static struct pci_driver ohci_pci_driver = { ++ name: "usb-ohci", ++ id_table: &ohci_pci_ids [0], ++ ++ probe: ohci_pci_probe, ++ remove: __devexit_p(ohci_pci_remove), ++ ++#ifdef CONFIG_PM ++ suspend: ohci_pci_suspend, ++ resume: ohci_pci_resume, ++#endif /* PM */ ++}; ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init ohci_hcd_init (void) ++{ ++ return pci_module_init (&ohci_pci_driver); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void __exit ohci_hcd_cleanup (void) ++{ ++ pci_unregister_driver (&ohci_pci_driver); ++} ++ ++module_init (ohci_hcd_init); ++module_exit (ohci_hcd_cleanup); ++ +diff -urN linux-2.4.26/drivers/usb/host/usb-ohci-sa1111.c linux-2.4.26-vrs1/drivers/usb/host/usb-ohci-sa1111.c +--- linux-2.4.26/drivers/usb/host/usb-ohci-sa1111.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/usb/host/usb-ohci-sa1111.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,118 @@ ++/* ++ * linux/drivers/usb/usb-ohci-sa1111.c ++ * ++ * The outline of this code was taken from Brad Parkers ++ * original OHCI driver modifications, and reworked into a cleaner form ++ * by Russell King . ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "usb-ohci.h" ++ ++int __devinit ++hc_add_ohci(struct pci_dev *dev, int irq, void *membase, unsigned long flags, ++ const char *name, const char *slot_name); ++extern void hc_remove_ohci(ohci_t *ohci); ++ ++static ohci_t *sa1111_ohci; ++ ++static void __init sa1111_ohci_configure(void) ++{ ++ unsigned int usb_rst = 0; ++ ++ if (machine_is_xp860() || ++ machine_has_neponset() || ++ machine_is_accelent_sa() || ++ machine_is_pfs168() || ++ machine_is_badge4()) ++ usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW; ++ ++ /* ++ * Configure the power sense and control lines. Place the USB ++ * host controller in reset. ++ */ ++ USB_RESET = usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET; ++ ++ /* ++ * Now, carefully enable the USB clock, and take ++ * the USB host controller out of reset. ++ */ ++ SKPCR |= SKPCR_UCLKEN; ++ udelay(11); ++ USB_RESET = usb_rst; ++} ++ ++static int __init sa1111_ohci_init(void) ++{ ++ int ret; ++ ++ /* ++ * Request memory resources. ++ */ ++// if (!request_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT, "usb-ohci")) ++// return -EBUSY; ++ ++ sa1111_ohci_configure(); ++ ++ /* ++ * Initialise the generic OHCI driver. ++ */ ++ ret = hc_add_ohci(SA1111_FAKE_PCIDEV, NIRQHCIM, ++ (void *)&USB_OHCI_OP_BASE, 0, &sa1111_ohci, ++ "usb-ohci", "sa1111"); ++ ++// if (ret) ++// release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT); ++ ++#ifdef CONFIG_SA1100_BADGE4 ++ if (machine_is_badge4() && (!ret)) { ++ /* found the controller, so now power the bus */ ++ badge4_set_5V(BADGE4_5V_USB, 1); ++ } ++#endif ++ ++ return ret; ++} ++ ++static void __exit sa1111_ohci_exit(void) ++{ ++ hc_remove_ohci(sa1111_ohci); ++ ++ /* ++ * Put the USB host controller into reset. ++ */ ++ USB_RESET |= USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET; ++ ++ /* ++ * Stop the USB clock. ++ */ ++ SKPCR &= ~SKPCR_UCLKEN; ++ ++ /* ++ * Release memory resources. ++ */ ++// release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT); ++ ++#ifdef CONFIG_SA1100_BADGE4 ++ if (machine_is_badge4()) { ++ badge4_set_5V(BADGE4_5V_USB, 0); ++ } ++#endif ++} ++ ++module_init(sa1111_ohci_init); ++module_exit(sa1111_ohci_exit); +diff -urN linux-2.4.26/drivers/usb/host/usb-ohci.c linux-2.4.26-vrs1/drivers/usb/host/usb-ohci.c +--- linux-2.4.26/drivers/usb/host/usb-ohci.c 2004-04-19 11:44:27.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/usb/host/usb-ohci.c 2004-04-19 15:27:57.000000000 +0100 +@@ -12,7 +12,6 @@ + * + * History: + * +- * 2002/10/22 OHCI_USB_OPER for ALi lockup in IBM i1200 (ALEX ) + * 2002/03/08 interrupt unlink fix (Matt Hughes), better cleanup on + * load failure (Matthew Frederickson) + * 2002/01/20 async unlink fixes: return -EINPROGRESS (per spec) and +@@ -81,16 +80,6 @@ + + #include "../hcd.h" + +-#ifdef CONFIG_PMAC_PBOOK +-#include +-#include +-#include +-#ifndef CONFIG_PM +-#define CONFIG_PM +-#endif +-#endif +- +- + /* + * Version Information + */ +@@ -98,12 +87,12 @@ + #define DRIVER_AUTHOR "Roman Weissgaerber , David Brownell" + #define DRIVER_DESC "USB OHCI Host Controller Driver" + +-/* For initializing controller (mask in an HCFS mode too) */ +-#define OHCI_CONTROL_INIT \ +- (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE +- + #define OHCI_UNLINK_TIMEOUT (HZ / 10) + ++static LIST_HEAD (ohci_hcd_list); ++spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; ++ ++ + /*-------------------------------------------------------------------------*/ + + /* AMD-756 (D2 rev) reports corrupt register contents in some cases. +@@ -130,57 +119,6 @@ + /*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ +- +-static void ohci_complete_add(struct ohci *ohci, struct urb *urb) +-{ +- +- if (urb->hcpriv != NULL) { +- printk("completing with non-null priv!\n"); +- return; +- } +- +- if (ohci->complete_tail == NULL) { +- ohci->complete_head = urb; +- ohci->complete_tail = urb; +- } else { +- ohci->complete_head->hcpriv = urb; +- ohci->complete_tail = urb; +- } +-} +- +-static inline struct urb *ohci_complete_get(struct ohci *ohci) +-{ +- struct urb *urb; +- +- if ((urb = ohci->complete_head) == NULL) +- return NULL; +- if (urb == ohci->complete_tail) { +- ohci->complete_tail = NULL; +- ohci->complete_head = NULL; +- } else { +- ohci->complete_head = urb->hcpriv; +- } +- urb->hcpriv = NULL; +- return urb; +-} +- +-static inline void ohci_complete(struct ohci *ohci) +-{ +- struct urb *urb; +- +- spin_lock(&ohci->ohci_lock); +- while ((urb = ohci_complete_get(ohci)) != NULL) { +- spin_unlock(&ohci->ohci_lock); +- if (urb->dev) { +- usb_dec_dev_use (urb->dev); +- urb->dev = NULL; +- } +- if (urb->complete) +- (*urb->complete)(urb); +- spin_lock(&ohci->ohci_lock); +- } +- spin_unlock(&ohci->ohci_lock); +-} + + /* free HCD-private data associated with this URB */ + +@@ -256,14 +194,20 @@ + } + + urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv); +- } else { +- if (urb->dev != NULL) { +- err ("Non-null dev at rm_priv time"); +- // urb->dev = NULL; +- } ++ usb_dec_dev_use (urb->dev); ++ urb->dev = NULL; + } + } + ++static void urb_rm_priv (struct urb * urb) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave (&usb_ed_lock, flags); ++ urb_rm_priv_locked (urb); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); ++} ++ + /*-------------------------------------------------------------------------*/ + + #ifdef DEBUG +@@ -484,7 +428,7 @@ + + static void ohci_dump (ohci_t *controller, int verbose) + { +- dbg ("OHCI controller usb-%s state", controller->ohci_dev->slot_name); ++ dbg ("OHCI controller usb-%s state", controller->slot_name); + + // dumps some of the state we know about + ohci_dump_status (controller); +@@ -507,6 +451,7 @@ + { + urb_priv_t * urb_priv = urb->hcpriv; + struct urb * urbt; ++ unsigned long flags; + int i; + + if (!urb_priv) +@@ -514,8 +459,7 @@ + + /* just to be sure */ + if (!urb->complete) { +- urb_rm_priv_locked (urb); +- ohci_complete_add(hc, urb); /* Just usb_dec_dev_use */ ++ urb_rm_priv (urb); + return -1; + } + +@@ -531,18 +475,20 @@ + usb_pipeout (urb->pipe) + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE); ++ + if (urb->interval) { + urb->complete (urb); +- ++ + /* implicitly requeued */ + urb->actual_length = 0; + urb->status = -EINPROGRESS; + td_submit_urb (urb); + } else { +- urb_rm_priv_locked (urb); +- ohci_complete_add(hc, urb); ++ urb_rm_priv(urb); ++ urb->complete (urb); + } +- break; ++ break; ++ + + case PIPE_ISOCHRONOUS: + for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next); +@@ -554,6 +500,7 @@ + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE); + urb->complete (urb); ++ spin_lock_irqsave (&usb_ed_lock, flags); + urb->actual_length = 0; + urb->status = USB_ST_URB_PENDING; + urb->start_frame = urb_priv->ed->last_iso + 1; +@@ -564,17 +511,18 @@ + } + td_submit_urb (urb); + } +- ++ spin_unlock_irqrestore (&usb_ed_lock, flags); ++ + } else { /* unlink URB, call complete */ +- urb_rm_priv_locked (urb); +- ohci_complete_add(hc, urb); ++ urb_rm_priv (urb); ++ urb->complete (urb); + } + break; + + case PIPE_BULK: + case PIPE_CONTROL: /* unlink URB, call complete */ +- urb_rm_priv_locked (urb); +- ohci_complete_add(hc, urb); ++ urb_rm_priv (urb); ++ urb->complete (urb); + break; + } + return 0; +@@ -594,7 +542,7 @@ + int i, size = 0; + unsigned long flags; + int bustime = 0; +- int mem_flags = GFP_ATOMIC; ++ int mem_flags = ALLOC_FLAGS; + + if (!urb->dev || !urb->dev->bus) + return -ENODEV; +@@ -611,24 +559,20 @@ + #ifdef DEBUG + urb_print (urb, "SUB", usb_pipein (pipe)); + #endif +- ++ + /* handle a request to the virtual root hub */ + if (usb_pipedevice (pipe) == ohci->rh.devnum) + return rh_submit_urb (urb); +- +- spin_lock_irqsave(&ohci->ohci_lock, flags); +- ++ + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); + usb_dec_dev_use (urb->dev); + return -ESHUTDOWN; + } + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); + usb_dec_dev_use (urb->dev); + return -ENOMEM; + } +@@ -650,7 +594,6 @@ + case PIPE_ISOCHRONOUS: /* number of packets from URB */ + size = urb->number_of_packets; + if (size <= 0) { +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); + usb_dec_dev_use (urb->dev); + return -EINVAL; + } +@@ -670,9 +613,8 @@ + + /* allocate the private part of the URB */ + urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), +- GFP_ATOMIC); ++ in_interrupt() ? GFP_ATOMIC : GFP_NOIO); + if (!urb_priv) { +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); + usb_dec_dev_use (urb->dev); + return -ENOMEM; + } +@@ -683,12 +625,13 @@ + urb_priv->ed = ed; + + /* allocate the TDs (updating hash chains) */ ++ spin_lock_irqsave (&usb_ed_lock, flags); + for (i = 0; i < size; i++) { + urb_priv->td[i] = td_alloc (ohci, SLAB_ATOMIC); + if (!urb_priv->td[i]) { + urb_priv->length = i; + urb_free_priv (ohci, urb_priv); +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + usb_dec_dev_use (urb->dev); + return -ENOMEM; + } +@@ -696,7 +639,7 @@ + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv (ohci, urb_priv); +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + usb_dec_dev_use (urb->dev); + return -EINVAL; + } +@@ -718,7 +661,7 @@ + } + if (bustime < 0) { + urb_free_priv (ohci, urb_priv); +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + usb_dec_dev_use (urb->dev); + return bustime; + } +@@ -744,6 +687,9 @@ + if (urb->timeout) { + struct list_head *entry; + ++ // FIXME: usb-uhci uses relative timeouts (like this), ++ // while uhci uses absolute ones (probably better). ++ // Pick one solution and change the affected drivers. + urb->timeout += jiffies; + + list_for_each (entry, &ohci->timeout_list) { +@@ -757,11 +703,10 @@ + + /* drive timeouts by SF (messy, but works) */ + writel (OHCI_INTR_SF, &ohci->regs->intrenable); +- (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + } + #endif + +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + + return 0; + } +@@ -792,7 +737,6 @@ + if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) + return rh_unlink_urb (urb); + +- spin_lock_irqsave(&ohci->ohci_lock, flags); + if (urb->hcpriv && (urb->status == USB_ST_URB_PENDING)) { + if (!ohci->disabled) { + urb_priv_t * urb_priv; +@@ -802,7 +746,6 @@ + */ + if (!(urb->transfer_flags & USB_ASYNC_UNLINK) + && in_interrupt ()) { +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); + err ("bug in call from %p; use async!", + __builtin_return_address(0)); + return -EWOULDBLOCK; +@@ -811,10 +754,11 @@ + /* flag the urb and its TDs for deletion in some + * upcoming SF interrupt delete list processing + */ ++ spin_lock_irqsave (&usb_ed_lock, flags); + urb_priv = urb->hcpriv; + + if (!urb_priv || (urb_priv->state == URB_DEL)) { +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + return 0; + } + +@@ -829,36 +773,26 @@ + + add_wait_queue (&unlink_wakeup, &wait); + urb_priv->wait = &unlink_wakeup; +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + + /* wait until all TDs are deleted */ + set_current_state(TASK_UNINTERRUPTIBLE); +- while (timeout && (urb->status == USB_ST_URB_PENDING)) { ++ while (timeout && (urb->status == USB_ST_URB_PENDING)) + timeout = schedule_timeout (timeout); +- set_current_state(TASK_UNINTERRUPTIBLE); +- } + set_current_state(TASK_RUNNING); + remove_wait_queue (&unlink_wakeup, &wait); + if (urb->status == USB_ST_URB_PENDING) { + err ("unlink URB timeout"); + return -ETIMEDOUT; + } +- +- usb_dec_dev_use (urb->dev); +- urb->dev = NULL; +- if (urb->complete) +- urb->complete (urb); + } else { + /* usb_dec_dev_use done in dl_del_list() */ + urb->status = -EINPROGRESS; +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + return -EINPROGRESS; + } + } else { +- urb_rm_priv_locked (urb); +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); +- usb_dec_dev_use (urb->dev); +- urb->dev = NULL; ++ urb_rm_priv (urb); + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + urb->status = -ECONNRESET; + if (urb->complete) +@@ -866,8 +800,6 @@ + } else + urb->status = -ENOENT; + } +- } else { +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); + } + return 0; + } +@@ -910,14 +842,14 @@ + * (freeing all the TDs, unlinking EDs) but we need + * to defend against bugs that prevent that. + */ +- spin_lock_irqsave(&ohci->ohci_lock, flags); ++ spin_lock_irqsave (&usb_ed_lock, flags); + for(i = 0; i < NUM_EDS; i++) { + ed = &(dev->ed[i]); + if (ed->state != ED_NEW) { + if (ed->state == ED_OPER) { + /* driver on that interface didn't unlink an urb */ + dbg ("driver usb-%s dev %d ed 0x%x unfreed URB", +- ohci->ohci_dev->slot_name, usb_dev->devnum, i); ++ ohci->slot_name, usb_dev->devnum, i); + ep_unlink (ohci, ed); + } + ep_rm_ed (usb_dev, ed); +@@ -925,7 +857,7 @@ + cnt++; + } + } +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + + /* if the controller is running, tds for those unlinked + * urbs get freed by dl_del_list at the next SF interrupt +@@ -962,7 +894,7 @@ + } else { + /* likely some interface's driver has a refcount bug */ + err ("bus %s devnum %d deletion in interrupt", +- ohci->ohci_dev->slot_name, usb_dev->devnum); ++ ohci->slot_name, usb_dev->devnum); + BUG (); + } + } +@@ -1259,12 +1191,17 @@ + td_t * td; + ed_t * ed_ret; + volatile ed_t * ed; ++ unsigned long flags; ++ ++ ++ spin_lock_irqsave (&usb_ed_lock, flags); + + ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | + (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]); + + if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { + /* pending delete request */ ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + return NULL; + } + +@@ -1277,6 +1214,7 @@ + /* out of memory */ + if (td) + td_free(ohci, td); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + return NULL; + } + ed->hwTailP = cpu_to_le32 (td->td_dma); +@@ -1299,7 +1237,8 @@ + ed->int_period = interval; + ed->int_load = load; + } +- ++ ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + return ed_ret; + } + +@@ -1340,7 +1279,6 @@ + /* enable SOF interrupt */ + writel (OHCI_INTR_SF, &ohci->regs->intrstatus); + writel (OHCI_INTR_SF, &ohci->regs->intrenable); +- (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + } + } + +@@ -1362,7 +1300,11 @@ + err("internal OHCI error: TD index > length"); + return; + } +- ++#ifdef CONFIG_SA1111 ++ if (data & (1 << 20)) ++ panic("td_fill: A20 = 1: %08x\n", data); ++#endif ++ + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; +@@ -1461,7 +1403,6 @@ + if (!ohci->sleeping) { + wmb(); + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ +- (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + } + break; + +@@ -1490,7 +1431,6 @@ + if (!ohci->sleeping) { + wmb(); + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ +- (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + } + break; + +@@ -1558,7 +1498,7 @@ + + /* handle an urb that is being unlinked */ + +-static void dl_del_urb (ohci_t *ohci, struct urb * urb) ++static void dl_del_urb (struct urb * urb) + { + wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait; + +@@ -1566,9 +1506,12 @@ + + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + urb->status = -ECONNRESET; +- ohci_complete_add(ohci, urb); ++ if (urb->complete) ++ urb->complete (urb); + } else { + urb->status = -ENOENT; ++ if (urb->complete) ++ urb->complete (urb); + + /* unblock sohci_unlink_urb */ + if (wait_head) +@@ -1587,7 +1530,10 @@ + td_t * td_rev = NULL; + td_t * td_list = NULL; + urb_priv_t * urb_priv = NULL; +- ++ unsigned long flags; ++ ++ spin_lock_irqsave (&usb_ed_lock, flags); ++ + td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + ohci->hcca->done_head = 0; + +@@ -1613,6 +1559,7 @@ + td_rev = td_list; + td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; + } ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + return td_list; + } + +@@ -1624,6 +1571,7 @@ + + static void dl_del_list (ohci_t * ohci, unsigned int frame) + { ++ unsigned long flags; + ed_t * ed; + __u32 edINFO; + __u32 tdINFO; +@@ -1631,6 +1579,8 @@ + __u32 * td_p; + int ctrl = 0, bulk = 0; + ++ spin_lock_irqsave (&usb_ed_lock, flags); ++ + for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) { + + tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0); +@@ -1651,7 +1601,7 @@ + + /* URB is done; clean up */ + if (++(urb_priv->td_cnt) == urb_priv->length) +- dl_del_urb (ohci, urb); ++ dl_del_urb (urb); + } else { + td_p = &td->hwNextTD; + } +@@ -1708,6 +1658,7 @@ + } + + ohci->ed_rm_list[frame] = NULL; ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + } + + +@@ -1724,7 +1675,9 @@ + struct urb * urb; + urb_priv_t * urb_priv; + __u32 tdINFO, edHeadP, edTailP; +- ++ ++ unsigned long flags; ++ + while (td_list) { + td_list_next = td_list->next_dl_td; + +@@ -1753,10 +1706,13 @@ + urb->status = cc_to_error[cc]; + sohci_return_urb (ohci, urb); + } else { +- dl_del_urb (ohci, urb); ++ spin_lock_irqsave (&usb_ed_lock, flags); ++ dl_del_urb (urb); ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + } + } + ++ spin_lock_irqsave (&usb_ed_lock, flags); + if (ed->state != ED_NEW) { + edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0; + edTailP = le32_to_cpup (&ed->hwTailP); +@@ -1765,6 +1721,7 @@ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + } ++ spin_unlock_irqrestore (&usb_ed_lock, flags); + + td_list = td_list_next; + } +@@ -1855,7 +1812,7 @@ + num_ports = roothub_a (ohci) & RH_A_NDP; + if (num_ports > MAX_ROOT_PORTS) { + err ("bogus NDP=%d for OHCI usb-%s", num_ports, +- ohci->ohci_dev->slot_name); ++ ohci->slot_name); + err ("rereads as NDP=%d", + readl (&ohci->regs->roothub.a) & RH_A_NDP); + /* retry later; "should not happen" */ +@@ -1955,8 +1912,7 @@ + int leni = urb->transfer_buffer_length; + int len = 0; + int status = TD_CC_NOERROR; +- unsigned long flags; +- ++ + __u32 datab[4]; + __u8 * data_buf = (__u8 *) datab; + +@@ -1965,16 +1921,13 @@ + __u16 wIndex; + __u16 wLength; + +- spin_lock_irqsave(&ohci->ohci_lock, flags); +- + if (usb_pipeint(pipe)) { + ohci->rh.urb = urb; + ohci->rh.send = 1; + ohci->rh.interval = urb->interval; + rh_init_int_timer(urb); + urb->status = cc_to_error [TD_CC_NOERROR]; +- +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); ++ + return 0; + } + +@@ -2146,7 +2099,6 @@ + #endif + + urb->hcpriv = NULL; +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); + usb_dec_dev_use (usb_dev); + urb->dev = NULL; + if (urb->complete) +@@ -2159,16 +2111,13 @@ + static int rh_unlink_urb (struct urb * urb) + { + ohci_t * ohci = urb->dev->bus->hcpriv; +- unsigned int flags; + +- spin_lock_irqsave(&ohci->ohci_lock, flags); + if (ohci->rh.urb == urb) { + ohci->rh.send = 0; + del_timer (&ohci->rh.rh_int_timer); + ohci->rh.urb = NULL; + + urb->hcpriv = NULL; +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); + usb_dec_dev_use(urb->dev); + urb->dev = NULL; + if (urb->transfer_flags & USB_ASYNC_UNLINK) { +@@ -2177,8 +2126,6 @@ + urb->complete (urb); + } else + urb->status = -ENOENT; +- } else { +- spin_unlock_irqrestore(&ohci->ohci_lock, flags); + } + return 0; + } +@@ -2213,16 +2160,12 @@ + writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + + dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;", +- ohci->ohci_dev->slot_name, ++ ohci->slot_name, + readl (&ohci->regs->control)); + + /* Reset USB (needed by some controllers) */ + writel (0, &ohci->regs->control); +- +- /* Force a state change from USBRESET to USBOPERATIONAL for ALi */ +- (void) readl (&ohci->regs->control); /* PCI posting */ +- writel (ohci->hc_control = OHCI_USB_OPER, &ohci->regs->control); +- ++ + /* HC Reset requires max 10 ms delay */ + writel (OHCI_HCR, &ohci->regs->cmdstatus); + while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { +@@ -2291,8 +2234,6 @@ + writel (RH_HS_LPSC, &ohci->regs->roothub.status); + #endif /* OHCI_USE_NPS */ + +- (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +- + // POTPGT delay is bits 24-31, in 2 ms units. + mdelay ((roothub_a (ohci) >> 23) & 0x1fe); + +@@ -2322,7 +2263,7 @@ + + static void check_timeouts (struct ohci *ohci) + { +- spin_lock (&ohci->ohci_lock); ++ spin_lock (&usb_ed_lock); + while (!list_empty (&ohci->timeout_list)) { + struct urb *urb; + +@@ -2335,15 +2276,15 @@ + continue; + + urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK; +- spin_unlock (&ohci->ohci_lock); ++ spin_unlock (&usb_ed_lock); + + // outside the interrupt handler (in a timer...) + // this reference would race interrupts + sohci_unlink_urb (urb); + +- spin_lock (&ohci->ohci_lock); ++ spin_lock (&usb_ed_lock); + } +- spin_unlock (&ohci->ohci_lock); ++ spin_unlock (&usb_ed_lock); + } + + +@@ -2357,8 +2298,6 @@ + struct ohci_regs * regs = ohci->regs; + int ints; + +- spin_lock (&ohci->ohci_lock); +- + /* avoid (slow) readl if only WDH happened */ + if ((ohci->hcca->done_head != 0) + && !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) { +@@ -2367,22 +2306,20 @@ + /* cardbus/... hardware gone before remove() */ + } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { + ohci->disabled++; +- spin_unlock (&ohci->ohci_lock); + err ("%s device removed!", ohci->ohci_dev->slot_name); + return; + + /* interrupt for some other device? */ + } else if ((ints &= readl (®s->intrenable)) == 0) { +- spin_unlock (&ohci->ohci_lock); + return; +- } ++ } + + // dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); + + if (ints & OHCI_INTR_UE) { + ohci->disabled++; + err ("OHCI Unrecoverable Error, controller usb-%s disabled", +- ohci->ohci_dev->slot_name); ++ ohci->slot_name); + // e.g. due to PCI Master/Target Abort + + #ifdef DEBUG +@@ -2398,39 +2335,26 @@ + + if (ints & OHCI_INTR_WDH) { + writel (OHCI_INTR_WDH, ®s->intrdisable); +- (void)readl (®s->intrdisable); /* PCI posting flush */ + dl_done_list (ohci, dl_reverse_done_list (ohci)); + writel (OHCI_INTR_WDH, ®s->intrenable); +- (void)readl (®s->intrdisable); /* PCI posting flush */ + } + + if (ints & OHCI_INTR_SO) { + dbg("USB Schedule overrun"); + writel (OHCI_INTR_SO, ®s->intrenable); +- (void)readl (®s->intrdisable); /* PCI posting flush */ + } + + // FIXME: this assumes SOF (1/ms) interrupts don't get lost... + if (ints & OHCI_INTR_SF) { + unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1; + writel (OHCI_INTR_SF, ®s->intrdisable); +- (void)readl (®s->intrdisable); /* PCI posting flush */ + if (ohci->ed_rm_list[!frame] != NULL) { + dl_del_list (ohci, !frame); + } +- if (ohci->ed_rm_list[frame] != NULL) { ++ if (ohci->ed_rm_list[frame] != NULL) + writel (OHCI_INTR_SF, ®s->intrenable); +- (void)readl (®s->intrdisable); /* PCI posting flush */ +- } + } + +- /* +- * Finally, we are done with trashing about our hardware lists +- * and other CPUs are allowed in. The festive flipping of the lock +- * ensues as we struggle with the check_timeouts disaster. +- */ +- spin_unlock (&ohci->ohci_lock); +- + if (!list_empty (&ohci->timeout_list)) { + check_timeouts (ohci); + // FIXME: enable SF as needed in a timer; +@@ -2442,9 +2366,6 @@ + + writel (ints, ®s->intrstatus); + writel (OHCI_INTR_MIE, ®s->intrenable); +- (void)readl (®s->intrdisable); /* PCI posting flush */ +- +- ohci_complete(ohci); + } + + /*-------------------------------------------------------------------------*/ +@@ -2475,10 +2396,14 @@ + ohci->regs = mem_base; + + ohci->ohci_dev = dev; ++#ifdef CONFIG_PCI + pci_set_drvdata(dev, ohci); ++#endif + ++ INIT_LIST_HEAD (&ohci->ohci_hcd_list); ++ list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); ++ + INIT_LIST_HEAD (&ohci->timeout_list); +- spin_lock_init(&ohci->ohci_lock); + + ohci->bus = usb_alloc_bus (&sohci_device_operations); + if (!ohci->bus) { +@@ -2501,7 +2426,7 @@ + + static void hc_release_ohci (ohci_t * ohci) + { +- dbg ("USB HC release ohci usb-%s", ohci->ohci_dev->slot_name); ++ dbg ("USB HC release ohci usb-%s", ohci->slot_name); + + /* disconnect all devices */ + if (ohci->bus->root_hub) +@@ -2514,7 +2439,9 @@ + free_irq (ohci->irq, ohci); + ohci->irq = -1; + } +- pci_set_drvdata(ohci->ohci_dev, NULL); ++#ifdef CONFIG_PCI ++ pci_set_drvdata(ohci->ohci_dev, 0); ++#endif + if (ohci->bus) { + if (ohci->bus->busnum != -1) + usb_deregister_bus (ohci->bus); +@@ -2522,6 +2449,9 @@ + usb_free_bus (ohci->bus); + } + ++ list_del (&ohci->ohci_hcd_list); ++ INIT_LIST_HEAD (&ohci->ohci_hcd_list); ++ + ohci_mem_cleanup (ohci); + + /* unmap the IO address space */ +@@ -2534,17 +2464,15 @@ + + /*-------------------------------------------------------------------------*/ + +-/* Increment the module usage count, start the control thread and +- * return success. */ +- +-static struct pci_driver ohci_pci_driver; +- +-static int __devinit +-hc_found_ohci (struct pci_dev *dev, int irq, +- void *mem_base, const struct pci_device_id *id) ++/* ++ * Host bus independent add one OHCI host controller. ++ */ ++int __devinit ++hc_add_ohci(struct pci_dev *dev, int irq, void *mem_base, unsigned long flags, ++ const char *name, const char *slot_name) + { +- ohci_t * ohci; + char buf[8], *bufp = buf; ++ ohci_t * ohci; + int ret; + + #ifndef __sparc__ +@@ -2554,34 +2482,17 @@ + #endif + printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n", + (unsigned long) mem_base, bufp); +- printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name); +- ++ + ohci = hc_alloc_ohci (dev, mem_base); + if (!ohci) { + return -ENOMEM; + } ++ ohci->slot_name = slot_name; + if ((ret = ohci_mem_init (ohci)) < 0) { + hc_release_ohci (ohci); + return ret; + } +- ohci->flags = id->driver_data; +- +- /* Check for NSC87560. We have to look at the bridge (fn1) to identify +- the USB (fn2). This quirk might apply to more or even all NSC stuff +- I don't know.. */ +- +- if(dev->vendor == PCI_VENDOR_ID_NS) +- { +- struct pci_dev *fn1 = pci_find_slot(dev->bus->number, PCI_DEVFN(PCI_SLOT(dev->devfn), 1)); +- if(fn1 && fn1->vendor == PCI_VENDOR_ID_NS && fn1->device == PCI_DEVICE_ID_NS_87560_LIO) +- ohci->flags |= OHCI_QUIRK_SUCKYIO; +- +- } +- +- if (ohci->flags & OHCI_QUIRK_SUCKYIO) +- printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n"); +- if (ohci->flags & OHCI_QUIRK_AMD756) +- printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n"); ++ ohci->flags = flags; + + if (hc_reset (ohci) < 0) { + hc_release_ohci (ohci); +@@ -2590,13 +2501,11 @@ + + /* FIXME this is a second HC reset; why?? */ + writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control); +- (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + wait_ms (10); + + usb_register_bus (ohci->bus); + +- if (request_irq (irq, hc_interrupt, SA_SHIRQ, +- ohci_pci_driver.name, ohci) != 0) { ++ if (request_irq (irq, hc_interrupt, SA_SHIRQ, name, ohci) != 0) { + err ("request interrupt %s failed", bufp); + hc_release_ohci (ohci); + return -EBUSY; +@@ -2604,7 +2513,7 @@ + ohci->irq = irq; + + if (hc_start (ohci) < 0) { +- err ("can't start usb-%s", dev->slot_name); ++ err ("can't start usb-%s", ohci->slot_name); + hc_release_ohci (ohci); + return -EBUSY; + } +@@ -2615,114 +2524,11 @@ + return 0; + } + +-/*-------------------------------------------------------------------------*/ +- +-#ifdef CONFIG_PM +- +-/* controller died; cleanup debris, then restart */ +-/* must not be called from interrupt context */ +- +-static void hc_restart (ohci_t *ohci) +-{ +- int temp; +- int i; +- +- if (ohci->pci_latency) +- pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency); +- +- ohci->disabled = 1; +- ohci->sleeping = 0; +- if (ohci->bus->root_hub) +- usb_disconnect (&ohci->bus->root_hub); +- +- /* empty the interrupt branches */ +- for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0; +- for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0; +- +- /* no EDs to remove */ +- ohci->ed_rm_list [0] = NULL; +- ohci->ed_rm_list [1] = NULL; +- +- /* empty control and bulk lists */ +- ohci->ed_isotail = NULL; +- ohci->ed_controltail = NULL; +- ohci->ed_bulktail = NULL; +- +- if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { +- err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp); +- } else +- dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name); +-} +- +-#endif /* CONFIG_PM */ +- +-/*-------------------------------------------------------------------------*/ +- +-/* configured so that an OHCI device is always provided */ +-/* always called with process context; sleeping is OK */ +- +-static int __devinit +-ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) ++/* ++ * Host bus independent remove one OHCI host controller. ++ */ ++void __devexit hc_remove_ohci(ohci_t *ohci) + { +- unsigned long mem_resource, mem_len; +- void *mem_base; +- int status; +- +- if (pci_enable_device(dev) < 0) +- return -ENODEV; +- +- if (!dev->irq) { +- err("found OHCI device with no IRQ assigned. check BIOS settings!"); +- pci_disable_device (dev); +- return -ENODEV; +- } +- +- /* we read its hardware registers as memory */ +- mem_resource = pci_resource_start(dev, 0); +- mem_len = pci_resource_len(dev, 0); +- if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) { +- dbg ("controller already in use"); +- pci_disable_device (dev); +- return -EBUSY; +- } +- +- mem_base = ioremap_nocache (mem_resource, mem_len); +- if (!mem_base) { +- err("Error mapping OHCI memory"); +- release_mem_region (mem_resource, mem_len); +- pci_disable_device (dev); +- return -EFAULT; +- } +- +- /* controller writes into our memory */ +- pci_set_master (dev); +- +- status = hc_found_ohci (dev, dev->irq, mem_base, id); +- if (status < 0) { +- iounmap (mem_base); +- release_mem_region (mem_resource, mem_len); +- pci_disable_device (dev); +- } +- return status; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-/* may be called from interrupt context [interface spec] */ +-/* may be called without controller present */ +-/* may be called with controller, bus, and devices active */ +- +-static void __devexit +-ohci_pci_remove (struct pci_dev *dev) +-{ +- ohci_t *ohci = pci_get_drvdata(dev); +- +- dbg ("remove %s controller usb-%s%s%s", +- hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), +- dev->slot_name, +- ohci->disabled ? " (disabled)" : "", +- in_interrupt () ? " in interrupt" : "" +- ); + #ifdef DEBUG + ohci_dump (ohci, 1); + #endif +@@ -2739,270 +2545,8 @@ + &ohci->regs->control); + + hc_release_ohci (ohci); +- +- release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0)); +- pci_disable_device (dev); +-} +- +- +-#ifdef CONFIG_PM +- +-/*-------------------------------------------------------------------------*/ +- +-static int +-ohci_pci_suspend (struct pci_dev *dev, u32 state) +-{ +- ohci_t *ohci = pci_get_drvdata(dev); +- unsigned long flags; +- u16 cmd; +- +- if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) { +- dbg ("can't suspend usb-%s (state is %s)", dev->slot_name, +- hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS)); +- return -EIO; +- } +- +- /* act as if usb suspend can always be used */ +- info ("USB suspend: usb-%s", dev->slot_name); +- ohci->sleeping = 1; +- +- /* First stop processing */ +- spin_lock_irqsave (&ohci->ohci_lock, flags); +- ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); +- writel (ohci->hc_control, &ohci->regs->control); +- writel (OHCI_INTR_SF, &ohci->regs->intrstatus); +- (void) readl (&ohci->regs->intrstatus); +- spin_unlock_irqrestore (&ohci->ohci_lock, flags); +- +- /* Wait a frame or two */ +- mdelay(1); +- if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF) +- mdelay (1); +- +-#ifdef CONFIG_PMAC_PBOOK +- if (_machine == _MACH_Pmac) +- disable_irq (ohci->irq); +- /* else, 2.4 assumes shared irqs -- don't disable */ +-#endif +- /* Enable remote wakeup */ +- writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable); +- +- /* Suspend chip and let things settle down a bit */ +- ohci->hc_control = OHCI_USB_SUSPEND; +- writel (ohci->hc_control, &ohci->regs->control); +- (void) readl (&ohci->regs->control); +- mdelay (500); /* No schedule here ! */ +- switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) { +- case OHCI_USB_RESET: +- dbg("Bus in reset phase ???"); +- break; +- case OHCI_USB_RESUME: +- dbg("Bus in resume phase ???"); +- break; +- case OHCI_USB_OPER: +- dbg("Bus in operational phase ???"); +- break; +- case OHCI_USB_SUSPEND: +- dbg("Bus suspended"); +- break; +- } +- /* In some rare situations, Apple's OHCI have happily trashed +- * memory during sleep. We disable it's bus master bit during +- * suspend +- */ +- pci_read_config_word (dev, PCI_COMMAND, &cmd); +- cmd &= ~PCI_COMMAND_MASTER; +- pci_write_config_word (dev, PCI_COMMAND, cmd); +-#ifdef CONFIG_PMAC_PBOOK +- { +- struct device_node *of_node; +- +- /* Disable USB PAD & cell clock */ +- of_node = pci_device_to_OF_node (ohci->ohci_dev); +- if (of_node) +- pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0); +- } +-#endif +- return 0; +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static int +-ohci_pci_resume (struct pci_dev *dev) +-{ +- ohci_t *ohci = pci_get_drvdata(dev); +- int temp; +- unsigned long flags; +- +- /* guard against multiple resumes */ +- atomic_inc (&ohci->resume_count); +- if (atomic_read (&ohci->resume_count) != 1) { +- err ("concurrent PCI resumes for usb-%s", dev->slot_name); +- atomic_dec (&ohci->resume_count); +- return 0; +- } +- +-#ifdef CONFIG_PMAC_PBOOK +- { +- struct device_node *of_node; +- +- /* Re-enable USB PAD & cell clock */ +- of_node = pci_device_to_OF_node (ohci->ohci_dev); +- if (of_node) +- pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1); +- } +-#endif +- +- /* did we suspend, or were we powered off? */ +- ohci->hc_control = readl (&ohci->regs->control); +- temp = ohci->hc_control & OHCI_CTRL_HCFS; +- +-#ifdef DEBUG +- /* the registers may look crazy here */ +- ohci_dump_status (ohci); +-#endif +- +- /* Re-enable bus mastering */ +- pci_set_master(ohci->ohci_dev); +- +- switch (temp) { +- +- case OHCI_USB_RESET: // lost power +- info ("USB restart: usb-%s", dev->slot_name); +- hc_restart (ohci); +- break; +- +- case OHCI_USB_SUSPEND: // host wakeup +- case OHCI_USB_RESUME: // remote wakeup +- info ("USB continue: usb-%s from %s wakeup", dev->slot_name, +- (temp == OHCI_USB_SUSPEND) +- ? "host" : "remote"); +- ohci->hc_control = OHCI_USB_RESUME; +- writel (ohci->hc_control, &ohci->regs->control); +- (void) readl (&ohci->regs->control); +- mdelay (20); /* no schedule here ! */ +- /* Some controllers (lucent) need a longer delay here */ +- mdelay (15); +- temp = readl (&ohci->regs->control); +- temp = ohci->hc_control & OHCI_CTRL_HCFS; +- if (temp != OHCI_USB_RESUME) { +- err ("controller usb-%s won't resume", dev->slot_name); +- ohci->disabled = 1; +- return -EIO; +- } +- +- /* Some chips likes being resumed first */ +- writel (OHCI_USB_OPER, &ohci->regs->control); +- (void) readl (&ohci->regs->control); +- mdelay (3); +- +- /* Then re-enable operations */ +- spin_lock_irqsave (&ohci->ohci_lock, flags); +- ohci->disabled = 0; +- ohci->sleeping = 0; +- ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; +- if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) { +- if (ohci->ed_controltail) +- ohci->hc_control |= OHCI_CTRL_CLE; +- if (ohci->ed_bulktail) +- ohci->hc_control |= OHCI_CTRL_BLE; +- } +- writel (ohci->hc_control, &ohci->regs->control); +- writel (OHCI_INTR_SF, &ohci->regs->intrstatus); +- writel (OHCI_INTR_SF, &ohci->regs->intrenable); +- /* Check for a pending done list */ +- writel (OHCI_INTR_WDH, &ohci->regs->intrdisable); +- (void) readl (&ohci->regs->intrdisable); +-#ifdef CONFIG_PMAC_PBOOK +- if (_machine == _MACH_Pmac) +- enable_irq (ohci->irq); +-#endif +- if (ohci->hcca->done_head) +- dl_done_list (ohci, dl_reverse_done_list (ohci)); +- writel (OHCI_INTR_WDH, &ohci->regs->intrenable); +- writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ +- writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ +- spin_unlock_irqrestore (&ohci->ohci_lock, flags); +- break; +- +- default: +- warn ("odd PCI resume for usb-%s", dev->slot_name); +- } +- +- /* controller is operational, extra resumes are harmless */ +- atomic_dec (&ohci->resume_count); +- +- return 0; +-} +- +-#endif /* CONFIG_PM */ +- +- +-/*-------------------------------------------------------------------------*/ +- +-static const struct pci_device_id __devinitdata ohci_pci_ids [] = { { +- +- /* +- * AMD-756 [Viper] USB has a serious erratum when used with +- * lowspeed devices like mice. +- */ +- vendor: 0x1022, +- device: 0x740c, +- subvendor: PCI_ANY_ID, +- subdevice: PCI_ANY_ID, +- +- driver_data: OHCI_QUIRK_AMD756, +- +-} , { +- +- /* handle any USB OHCI controller */ +- class: ((PCI_CLASS_SERIAL_USB << 8) | 0x10), +- class_mask: ~0, +- +- /* no matter who makes it */ +- vendor: PCI_ANY_ID, +- device: PCI_ANY_ID, +- subvendor: PCI_ANY_ID, +- subdevice: PCI_ANY_ID, +- +- }, { /* end: all zeroes */ } +-}; +- +-MODULE_DEVICE_TABLE (pci, ohci_pci_ids); +- +-static struct pci_driver ohci_pci_driver = { +- name: "usb-ohci", +- id_table: &ohci_pci_ids [0], +- +- probe: ohci_pci_probe, +- remove: __devexit_p(ohci_pci_remove), +- +-#ifdef CONFIG_PM +- suspend: ohci_pci_suspend, +- resume: ohci_pci_resume, +-#endif /* PM */ +-}; +- +- +-/*-------------------------------------------------------------------------*/ +- +-static int __init ohci_hcd_init (void) +-{ +- return pci_module_init (&ohci_pci_driver); + } + +-/*-------------------------------------------------------------------------*/ +- +-static void __exit ohci_hcd_cleanup (void) +-{ +- pci_unregister_driver (&ohci_pci_driver); +-} +- +-module_init (ohci_hcd_init); +-module_exit (ohci_hcd_cleanup); +- +- + MODULE_AUTHOR( DRIVER_AUTHOR ); + MODULE_DESCRIPTION( DRIVER_DESC ); + MODULE_LICENSE("GPL"); +diff -urN linux-2.4.26/drivers/usb/host/usb-ohci.h linux-2.4.26-vrs1/drivers/usb/host/usb-ohci.h +--- linux-2.4.26/drivers/usb/host/usb-ohci.h 2004-04-19 11:44:27.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/usb/host/usb-ohci.h 2004-04-19 17:13:29.000000000 +0100 +@@ -384,12 +384,13 @@ + #define OHCI_QUIRK_SUCKYIO 0x02 /* NSC superio */ + + struct ohci_regs * regs; /* OHCI controller's memory */ ++ struct list_head ohci_hcd_list; /* list of all ohci_hcd */ + ++ struct ohci * next; // chain of ohci device contexts + struct list_head timeout_list; + // struct list_head urb_list; // list of all pending urbs +- spinlock_t ohci_lock; /* Covers all fields up & down */ +- struct urb *complete_head, *complete_tail; +- ++ // spinlock_t urb_list_lock; // lock to keep consistency ++ + int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/ + ed_t * ed_rm_list[2]; /* lists of all endpoints to be removed */ + ed_t * ed_bulktail; /* last endpoint of bulk list */ +@@ -403,6 +404,7 @@ + + /* PCI device handle, settings, ... */ + struct pci_dev *ohci_dev; ++ const char *slot_name; + u8 pci_latency; + struct pci_pool *td_cache; + struct pci_pool *dev_cache; +@@ -448,7 +450,7 @@ + #endif + + #ifndef CONFIG_PCI +-# error "usb-ohci currently requires PCI-based controllers" ++//# error "usb-ohci currently requires PCI-based controllers" + /* to support non-PCI OHCIs, you need custom bus/mem/... glue */ + #endif + +@@ -641,3 +643,6 @@ + pci_pool_free (hc->dev_cache, dev, dev->dma); + } + ++/* For initializing controller (mask in an HCFS mode too) */ ++#define OHCI_CONTROL_INIT \ ++ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE +diff -urN linux-2.4.26/drivers/video/Config.in linux-2.4.26-vrs1/drivers/video/Config.in +--- linux-2.4.26/drivers/video/Config.in 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/video/Config.in 2004-02-23 23:25:16.000000000 +0000 +@@ -30,13 +30,28 @@ + tristate ' Permedia3 support (EXPERIMENTAL)' CONFIG_FB_PM3 + fi + fi +- if [ "$CONFIG_ARCH_ACORN" = "y" ]; then +- bool ' Acorn VIDC support' CONFIG_FB_ACORN +- fi +- dep_tristate ' Cyber2000 support' CONFIG_FB_CYBER2000 $CONFIG_PCI +- if [ "$CONFIG_ARCH_SA1100" = "y" ]; then +- bool ' SA-1100 LCD support' CONFIG_FB_SA1100 ++ if [ "$CONFIG_ARM" = "y" ]; then ++ if [ "$CONFIG_ARCH_ACORN" -o "$CONFIG_ARCH_RISCSTATION" ]; then ++ bool ' Acorn VIDC support' CONFIG_FB_ACORN ++ else ++ define_bool CONFIG_FB_ACORN n ++ fi ++ dep_bool ' Anakin LCD support' CONFIG_FB_ANAKIN $CONFIG_ARCH_ANAKIN ++ dep_bool ' CLPS711X LCD support' CONFIG_FB_CLPS711X $CONFIG_ARCH_CLPS711X ++ dep_bool ' SA-1100 LCD support' CONFIG_FB_SA1100 $CONFIG_ARCH_SA1100 ++ dep_bool ' MX1ADS LCD support' CONFIG_FB_DBMX1 $CONFIG_ARCH_MX1ADS ++ if [ "$CONFIG_FB_SA1100" = "y" -a "$CONFIG_SA1100_CERF" = "y" ]; then ++ choice 'CerfBoard LCD Display Size' \ ++ "3.8_Color CONFIG_CERF_LCD_38_A \ ++ 3.8_Mono CONFIG_CERF_LCD_38_B \ ++ 5.7 CONFIG_CERF_LCD_57_A \ ++ 7.2 CONFIG_CERF_LCD_72_A" 5.7 ++ fi ++ if [ "$CONFIG_FB_SA1100" = "y" -a "$CONFIG_SA1100_CERF_CPLD" = "y" ]; then ++ bool 'Cerfboard Backlight (CerfPDA)' CONFIG_SA1100_CERF_LCD_BACKLIGHT ++ fi + fi ++ dep_tristate ' CyberPro 2000/2010/5000 support' CONFIG_FB_CYBER2000 $CONFIG_PCI + if [ "$CONFIG_APOLLO" = "y" ]; then + define_bool CONFIG_FB_APOLLO y + fi +@@ -265,7 +280,7 @@ + "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_RETINAZ3" = "y" -o \ + "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ + "$CONFIG_FB_BWTWO" = "y" -o "$CONFIG_FB_CLGEN" = "y" -o \ +- "$CONFIG_FB_TX3912" = "y" ]; then ++ "$CONFIG_FB_TX3912" = "y" -o "$CONFIG_FB_CLPS711X" = "y" ]; then + define_tristate CONFIG_FBCON_MFB y + else + if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_AMIGA" = "m" -o \ +@@ -273,19 +288,20 @@ + "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_RETINAZ3" = "m" -o \ + "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ + "$CONFIG_FB_BWTWO" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \ +- "$CONFIG_FB_TX3912" = "m" ]; then ++ "$CONFIG_FB_TX3912" = "m" -o "$CONFIG_FB_CLPS711X" = "m" ]; then + define_tristate CONFIG_FBCON_MFB m + fi + fi + if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_MAC" = "y" -o \ + "$CONFIG_FB_SA1100" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \ +- "$CONFIG_FB_TX3912" = "y" ]; then ++ "$CONFIG_FB_TX3912" = "y" -o "$CONFIG_FB_CLPS711X" = "y" -o \ ++ "$CONFIG_FB_DBMX1" = "y" ]; then + define_tristate CONFIG_FBCON_CFB2 y + define_tristate CONFIG_FBCON_CFB4 y + else + if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_MAC" = "m" -o \ + "$CONFIG_FB_SA1100" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \ +- "$CONFIG_FB_TX3912" = "m" ]; then ++ "$CONFIG_FB_TX3912" = "m" -o "$CONFIG_FB_CLPS711X" = "m" ]; then + define_tristate CONFIG_FBCON_CFB2 m + define_tristate CONFIG_FBCON_CFB4 m + fi +@@ -312,7 +328,8 @@ + "$CONFIG_FB_TX3912" = "y" -o \ + "$CONFIG_FB_SIS" = "y" -o "$CONFIG_FB_NEOMAGIC" = "y" -o \ + "$CONFIG_FB_STI" = "y" -o "$CONFIG_FB_HP300" = "y" -o \ +- "$CONFIG_FB_INTEL" = "y" ]; then ++ "$CONFIG_FB_INTEL" = "y" -o \ ++ "$CONFIG_FB_DBMX1" = "y" ]; then + define_tristate CONFIG_FBCON_CFB8 y + else + if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \ +@@ -354,7 +371,9 @@ + "$CONFIG_FB_CYBER2000" = "y" -o "$CONFIG_FB_3DFX" = "y" -o \ + "$CONFIG_FB_SIS" = "y" -o "$CONFIG_FB_SA1100" = "y" -o \ + "$CONFIG_FB_PVR2" = "y" -o "$CONFIG_FB_VOODOO1" = "y" -o \ +- "$CONFIG_FB_NEOMAGIC" = "y" -o "$CONFIG_FB_INTEL" = "y" ]; then ++ "$CONFIG_FB_NEOMAGIC" = "y" -o "$CONFIG_FB_INTEL" = "y" -o \ ++ "$CONFIG_FB_ANAKIN" = "y" -o \ ++ "$CONFIG_FB_DBMX1" = "y" ]; then + define_tristate CONFIG_FBCON_CFB16 y + else + if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ +@@ -509,6 +528,9 @@ + if [ "$CONFIG_AMIGA" = "y" ]; then + define_bool CONFIG_FONT_PEARL_8x8 y + fi ++ if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_RISCSTATION" = "y" ]; then ++ define_bool CONFIG_FONT_ACORN_8x8 y ++ fi + if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_ACORN" = "y" ]; then + define_bool CONFIG_FONT_ACORN_8x8 y + fi +diff -urN linux-2.4.26/drivers/video/Makefile linux-2.4.26-vrs1/drivers/video/Makefile +--- linux-2.4.26/drivers/video/Makefile 2004-02-27 20:03:27.000000000 +0000 ++++ linux-2.4.26-vrs1/drivers/video/Makefile 2004-02-23 13:36:35.000000000 +0000 +@@ -55,6 +55,7 @@ + obj-$(CONFIG_FB_PLATINUM) += platinumfb.o + obj-$(CONFIG_FB_VALKYRIE) += valkyriefb.o + obj-$(CONFIG_FB_CT65550) += chipsfb.o ++obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o + obj-$(CONFIG_FB_CYBER) += cyberfb.o + obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o + obj-$(CONFIG_FB_SGIVW) += sgivwfb.o +@@ -68,7 +69,7 @@ + obj-$(CONFIG_FB_TRIDENT) += tridentfb.o fbgen.o + obj-$(CONFIG_FB_S3TRIO) += S3triofb.o + obj-$(CONFIG_FB_TGA) += tgafb.o fbgen.o +-obj-$(CONFIG_FB_VESA) += vesafb.o ++obj-$(CONFIG_FB_VESA) += vesafb.o + obj-$(CONFIG_FB_VGA16) += vga16fb.o fbcon-vga-planes.o + obj-$(CONFIG_FB_VIRGE) += virgefb.o + obj-$(CONFIG_FB_G364) += g364fb.o +@@ -126,14 +127,16 @@ + + obj-$(CONFIG_FB_SUN3) += sun3fb.o + obj-$(CONFIG_FB_BWTWO) += bwtwofb.o +-obj-$(CONFIG_FB_HGA) += hgafb.o ++obj-$(CONFIG_FB_HGA) += hgafb.o + obj-$(CONFIG_FB_SA1100) += sa1100fb.o +-obj-$(CONFIG_FB_VIRTUAL) += vfb.o ++obj-$(CONFIG_FB_DBMX1) += dbmx1fb.o ++obj-$(CONFIG_FB_VIRTUAL) += vfb.o + obj-$(CONFIG_FB_HIT) += hitfb.o fbgen.o + obj-$(CONFIG_FB_E1355) += epson1355fb.o fbgen.o + obj-$(CONFIG_FB_E1356) += epson1356fb.o + obj-$(CONFIG_FB_PVR2) += pvr2fb.o + obj-$(CONFIG_FB_VOODOO1) += sstfb.o ++obj-$(CONFIG_FB_ANAKIN) += anakinfb.o + + # Generic Low Level Drivers + +@@ -169,4 +172,3 @@ + -e 's/dfont\(_uni.*\]\)/promfont\1 __initdata/' > promcon_tbl.c + + promcon_tbl.o: promcon_tbl.c $(TOPDIR)/include/linux/types.h +- +diff -urN linux-2.4.26/drivers/video/acornfb.c linux-2.4.26-vrs1/drivers/video/acornfb.c +--- linux-2.4.26/drivers/video/acornfb.c 2002-08-03 01:39:45.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/video/acornfb.c 2004-01-14 21:32:26.000000000 +0000 +@@ -752,11 +752,12 @@ + } + #endif + #ifdef FBCON_HAS_CFB16 +- if (bpp == 16 && regno < 16) { ++ if (bpp == 16) { + int i; + +- current_par.cmap.cfb16[regno] = +- regno | regno << 5 | regno << 10; ++ if (regno < 16) ++ current_par.cmap.cfb16[regno] = ++ regno | regno << 5 | regno << 10; + + pal.p = 0; + vidc_writel(0x10000000); +@@ -1215,7 +1216,7 @@ + p.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green; + p.vidc20.blue = current_par.palette[(i >> 2) & 31].vidc20.blue; + } +- acornfb_palette_write(i, current_par.palette[i]); ++ acornfb_palette_write(i, p); + } + } else + #endif +diff -urN linux-2.4.26/drivers/video/anakinfb.c linux-2.4.26-vrs1/drivers/video/anakinfb.c +--- linux-2.4.26/drivers/video/anakinfb.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.4.26-vrs1/drivers/video/anakinfb.c 2004-01-14 21:32:26.000000000 +0000 +@@ -0,0 +1,221 @@ ++/* ++ * linux/drivers/video/anakinfb.c ++ * ++ * Copyright (C) 2001 Aleph One Ltd. for Acunia N.V. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Changelog: ++ * 23-Apr-2001 TTC Created ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include